-
-
Notifications
You must be signed in to change notification settings - Fork 9.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Bug]: RSC stories that use "react-server" exports do not get bundled correctly #27527
Comments
@kasperpeulen @valentinpalkovic @ndelangen can we get storybook to bundle |
Thanks for the detailed issue! @shawngustaw For now I can recommend the following work-around: // main.ts
webpackFinal: (config) => {
config.resolve?.conditionNames?.unshift('react-server')
config.resolve!.alias = {
...config.resolve?.alias,
'client-only': false
}
return config;
} I'm going to investigate how to best solve this in general. This is our RSC demo that uses an in-memory database in storybook: Running both the server and client stuff in one environment (the browser) has a lot of advantages as it comes to a true unit-testing experience. It is super fast, and you can both set a server mock, spy on a function and access a dom element in the same unit/component test: My feeling is that the best thing we can do, is trick the bundler, to think of storybook as both a react-server and a react-client in the same time, which is what the above tries to achieve. |
@kasperpeulen Thanks so much for tracking that down. If we can't safely apply this configuration automatically by default, at least we can add that configuration (and its Vite equivalent) prominently in our RSC docs once that's a thing. cc @kylegach |
@shilman After thinking a bit more about it. Maybe we should just do add it to the nextjs framework by default when I can imagine that in theory this does unwanted stuff, for example, when a library exports both a RSC and client variant of the same component. I only don't know if this is really what the ecosystem is gonna do, or that this is just an imaginary example. In this worst case scenario, I think we should recommend people to have 2 storybooks. One that is for RSC components, and one for client only components. |
Good call @kasperpeulen ! @shawngustaw is that a change you might be able to contribute? |
I can provide two examples (I'm sure there are more and will be many more in the future) that do this type of exporting. Our team has been trying work around issues for a few days because we rely on these packages but also want to have storybook coverage for components/pages that use them.
With the recommended work-around these packages throw warnings for exports that are not found and stories with components using these imports fail to render with webpack import errors. WARN export 'useForm' (imported as 'useForm') was not found in 'react-hook-form' (possible exports: appendErrors, get, set)
WARN export 'useForm' (imported as 'useForm') was not found in 'react-hook-form' (possible exports: appendErrors, get, set)
WARN export 'FormProvider' (imported as 'FormProvider') was not found in 'react-hook-form' (possible exports: appendErrors, get, set)
WARN export 'Controller' (imported as 'Controller') was not found in 'react-hook-form' (possible exports: appendErrors, get, set)
WARN export 'useFormContext' (imported as 'useFormContext') was not found in 'react-hook-form' (possible exports: appendErrors, get, set)
WARN export 'useForm' (imported as 'useForm') was not found in 'react-hook-form' (possible exports: appendErrors, get, set)
WARN export 'useForm' (imported as 'useForm') was not found in 'react-hook-form' (possible exports: appendErrors, get, set)
WARN export 'ApolloNextAppProvider' (imported as 'ApolloNextAppProvider') was not found in '@apollo/experimental-nextjs-app-support' (possible exports: ApolloClient, DebounceMultipartResponsesLink, InMemoryCache, RemoveMultipartDirectivesLink, SSRMultipartLink, built_for_rsc, registerApolloClient) |
@BradMcGonigle It seems that it might be best to not use: config.resolve?.conditionNames?.unshift('react-server') Another workaround would be: webpackFinal: (config) => {
config.resolve!.alias = {
...config.resolve?.alias,
"@apollo/experimental-nextjs-app-support/rsc$": path.resolve(
__dirname,
"../node_modules/@apollo/client-react-streaming/dist/index.rsc",
),
};
return config;
}, And then always import registerApolloClient from the rsc subpath: import { registerApolloClient } from "@apollo/experimental-nextjs-app-support/rsc"; This will make sure it works both in storybook and in the live production. @shilman We should probably contact Apollo, and discuss better solutions. |
We will remove the RSC subpath in the next release, as we want to simplify this for our normal users 😞 I think the people to contact here are probably the React team, since they are the ones who have a working "dual land" webpack plugin that can load files twice in both contexts. Maybe they have an idea (they came up with the whole thing after all). |
I'm not sure if that is really more simple. If you import Splitting the exports into different import subpaths seems safer and more straight-forward to me, even outside of the storybook context.
The problem is that there is only one context in storybook. The browser. We never run RSC in the server. And async components work similarly in the browser and the server. They are not recommended to run in the browser for production use cases, but the React team says, they might support that in the future as well:
We belief that running async components in the browser gives the most straigt-forward unit testing experience, without having to setup a server. We have plan to polyfill the Node API's in the browser, to make this experience easier, and have recipes to mock out the database with an in-memory database. Having two different systems, a browser and a server, puts you really into the E2E kind of space. Which is very useful, but with different tradeoffs. For example, you lose the control and flexibility you have in unit testing, where you can just access any variable and call any function/Component. If you are gonna mock or spy in a unit test. Are you importing the mock from the server or the browser? Do you import the Date global from the server or the browser etc. We believe that for a storybook, where you develop/test and document components in isolation, having only to deal with one runtime makes the most sense. If you belief (and other libraries belief) that having both RSC and browser exports in one sub-path, is the best pattern. Add a dual "exports": {
".": {
"import": {
"types": "./dist/combined.d.ts",
"react-server": {
// for combined RSC/browser environments like storybook, possibly vitest browser mode as well
"browser": "./dist/combined.js",
"default": "./dist/index.rsc.js",
},
"edge-light": "./dist/index.ssr.js",
"browser": "./dist/index.browser.js",
"node": "./dist/index.ssr.js"
}
}, If you agree, I can make a PR for that. |
The same is true for the As for the "more simple" part: a lot of the exports are available in all variants, but with different implementations per condition. Previously, we had users import from
You can RSC in the browser as well, using the RSC build of React, and then send the result to the SSR build of React, and then hydrate it with the Browser build of React - all three steps in the same browser. (Preferrably in separated workers) I'm really sorry, I know you deserve a longer answer, but it's very late here and I'm very tired. Generally, it boils down to this: This is a pattern that the React team intends to be used throughout the ecosystem and that they are modelling themselves. I don't want Apollo Client to become the place this is fought out on. We had enough discussions around bundling with the React and Next.js team over the last year. PS: Rereading this a bit more: I see where you are coming from with the idea of "only one runtime" - but running RSC code and Browser code in the same environment/scope seems very dangerous to me. The idea of the "combined" export would really be an export just for storybook and we'd have to write a completely separate implementation that ignored normal React behavior to work under the constraints you create in StoryBook that are not shared by any real-life deployment of a React application. You can't expect every React library to do that 😕 PPS: one more example where this is dangerous:
|
Like @phryneas I think tackling this from the React side is probably the only real "solution" without playing wack-a-mole as more packages adopt react-server conditional exports. If there are discussion with the the React team please reference this issue because I'd love to follow along and contribute to the discussion if possible. |
@yannbf importing |
I might be missing a part of the conversation here, but as I said before, running It expects to run in |
I have no time at the moment to write an adequate reply. But I do appreciate your feedback, @phryneas. The point that even We are still discussing how we can address this, but we are too busy releasing 8.3 at the moment. So I will write a more proper longer reaction later! |
Just wanted to add a +1 to that, while the sort of mocking of RSC environment in the browser is workable, I think storybook is likely to continue to hit these kinds of issues in the future. It's a tough spot to be in. Appreciate all the thought and discussion happening here. I think @BradMcGonigle and I may have a workaround that can keep us afloat for a bit, but agree it feels like a more ideal solve is to handle the conditional export more explicitly. |
Describe the bug
Hi there! 👋
Apollo has a package for integrating streaming + apollo client found here: https://github.com/apollographql/apollo-client-nextjs/
Recently they migrated to
react-server
conditional exports (RFC found here: https://github.com/reactjs/rfcs/blob/main/text/0227-server-module-conventions.md#react-server-conditional-exports) such that code that's supposed to only run on the server never gets imported in a client bundle. This obviously breaks in storybook where the RSC implementation is actually running in the browser. Does anyone have any suggestions around how to make this work? I expect more and more packages are going to usereact-server
for exports/bundling so it feels like this is something storybook should be prepared to handle.I provided a simple repo below that could be a starting point for coming up with a fix - it's just a barebones Next app with storybook 8 installed.
Reproduction link
https://github.com/shawngustaw/storybook-rsc-repro
Reproduction steps
Pokemon
component renders correctly in NextJS app router using RSC'sSystem
Additional context
I brought this up with the Apollo team here: apollographql/apollo-client-nextjs#307
No response
The text was updated successfully, but these errors were encountered: