Storybook for React Server Parts
React Server Components (RSC) are a brand new programming mannequin for React-based net UIs. In distinction to conventional React “shopper” elements, they solely render on the server. This results in a wide range of efficiency and safety advantages, however it is usually an enormous departure from the React instruments and libraries we use right now.
One of the crucial impacted areas is component-driven improvement and testing. Instruments like Storybook, Testing Library, and Playwright/Cypress Part Testing all assume that the person’s elements are being rendered within the browser (or JSDom). However with server elements, that’s not the case.
This creates the query: what does it imply to do remoted element improvement and testing for the server?
At the moment, I’m excited to launch RSC assist in Storybook’s Subsequent.js framework as an experimental reply to this query. It’s a purely client-side implementation, making it suitable with the complete ecosystem of Storybook addons and integrations.
Learn on to study the way it works, the right way to use it, and how one can strive it right now!
Servers are from Mars, shoppers are from Venus
RSCs have two main variations from conventional shopper elements, each of that are current within the following instance:
// ApiCard.tsx
import { ComponentProps } from 'react';
import { Card } from './Card';
import { findById } from './db';
export async perform DbCard({ id }: {id: quantity}) {
let props;
strive {
const contact = await findById(id);
props = { state: 'success', contact };
} catch (e) {
props = { state: 'error' };
}
return <Card {...props} />;
}
- The primary distinction is that our element is
async
, which isn’t supported on the shopper. - The second distinction is that our element can entry Node code straight, on this case the
findById
perform that wraps an authenticated database connection.
RSC does loads underneath the hood to implement these two variations. This code solely ever runs on the server, and it generates a static JSON-like construction which is streamed right down to the shopper.
Storybook is a pure shopper software. It produces a static construct of pure HTML/CSS/JS with no Node in sight! So, supporting RSC would require determining both the right way to get RSCs to render on the shopper OR rearchitecting Storybook for servers.
We began by specializing in the shopper strategy. We wish to reduce influence to our customers, who’ve written tens of millions of tales and tons of of addons, all primarily based on the present structure.
So, how on earth does it work?
Getting async with it
The primary problem to getting RSCs to render on the shopper is configuring the right way to assist async elements. It seems that that is already supported (unofficially) in Subsequent.js’s canary React model. Particular because of JamesManningR and julRuss, who contributed this easy answer!
import { Suspense } from 'react';
export const ClientContact = ({ id }) => (
<Suspense><DbCard id={id} /></Suspense>
);
Beginning in Storybook 8, @storybook/nextjs
can wrap your tales in Suspense
utilizing the experimentalNextRSC
characteristic flag in .storybook/most important.js
:
// .storybook/most important.js
export default {
options: {
experimentalNextRSC: true,
}
};
You too can do that manually in 7.x variations of @storybook/nextjs
by wrapping your RSC tales in a decorator.
Observe: This answer doesn’t but work in different Storybook React frameworks (e.g. react-vite
, react-webpack5
) as a result of they don’t use Subsequent.js’s canary model of React. Hopefully, this limitation is eliminated by the following model of React.
Mocked and loaded
Fixing the async downside solely will get us midway there. Our DbCard
element additionally references node code which fetches the info to populate the element. This can be a downside within the browser, which can not execute Node code!
To work round this downside, we advocate establishing a clear knowledge entry layer. That is additionally recommended as a best practice by the architect of RSC.
Upon getting the info entry layer, you may mock it out in order that it might run within the browser and so that you could exactly management the info that it returns to train totally different UI states (loading, error, success, and so forth.).
You’ll be able to mock the info entry layer utilizing module mocks or community mocks, each of that are supported in Storybook.
Modules: There’s a neighborhood addon, storybook-addon-module-mock, that gives jest.mock
-style mocking (for Webpack initiatives solely). You too can use webpack/vite aliases for a less complicated however extra restricted answer. We plan to offer ergonomic module mocking in a future model of Storybook.
Community APIs: To mock community requests, we advocate Mock Service Worker (msw). Storybook additionally helps quite a few different network and GraphQL mocking addons.
Bringing this again to our instance, right here’s what a narrative would possibly appear to be utilizing storybook-addon-module-mock
:
// DbCard.tales.js
import { StoryObj, Meta } from '@storybook/react';
import { createMock } from 'storybook-addon-module-mock';
import { DbCard } from './DbCard';
import * as db from './db';
export default { element: DbCard };
export const Success {
args: { id: 1 },
parameters: {
moduleMock: {
mock: () => {
const mock = createMock(db, 'findById');
mock.mockReturnValue(Promise.resolve({
identify: 'Beyonce',
img: 'https://blackhistorywall.recordsdata.wordpress.com/2010/02/picture-device-independent-bitmap-119.jpg',
tel: '+123 456 789',
electronic mail: 'b@beyonce.com'
}))
return [mock];
},
},
},
}
Full demo: API + module mocking
For the complete instance above together with each the module mocked database model and the MSW2-mocked API model, please verify our full RSC demo Storybook or its GitHub repo.
What’s the catch?
On this submit, we’ve efficiently written a narrative for our first RSC in Storybook and proven how that is all applied underneath the hood.
This was all fairly easy, however the strategy has limitations:
- Constancy. The pure shopper implementation differs dramatically from the server-side, streaming RSC implementation that’s working in your software.
- Comfort. The mocking options right here can undoubtedly be improved. Not solely is our present module mocking answer verbose, but it surely doesn’t play properly with Storybook args/controls.
We plan to work on each of those limitations in subsequent iterations, which is why we’ve labeled this answer as experimental.
Use Storybook for RSC right now 🎊
To make use of Storybook for RSC, improve your Storybook to eight.0-alpha:
npx storybook@subsequent improve --prerelease
Then, allow the experimental characteristic in your .storybook/most important.ts
:
// .storybook/most important.js
export default {
options: {
experimentalNextRSC: true,
},
};
For extra data, see the @storybook/nextjs
README.
That is the primary of our posts detailing the contents of Storybook 8.0, our subsequent main model, and we’ll have heaps extra to return within the months forward. Sustain with all of the information on the following launch by following us on social media or signing up for the Storybook newsletter!