Open ghost opened 1 year ago
@bjoanne Thanks for starting this discussion! A few questions:
For the most part, CDS components are client components. This table from the NextJS docs illustrates this boundary.
Most teams we've heard of using NextJS 13 are wrapping our components in their own Client Component(s) for now until we add the "use client"
directive.
Composing a server component within children
is also an option that most components support.
...until we add the
"use client"
directive.
Is there a roadmap for this?
...until we add the
"use client"
directive.Is there a roadmap for this?
@kiriny Not right now. It's still a canary feature in React 18.2.0 and we'll probably wait until it's no longer experimental. This excerpt from the docs is relevant though:
Libraries published to npm should include
'use client'
on exported React components that can be rendered with serializable props that use client-only React features, to allow those components to be imported and rendered by server components. Otherwise, users will need to wrap library components in their own'use client'
files which can be cumbersome and prevents the library from moving logic to the server later. When publishing prebundled files to npm, ensure that'use client'
source files end up in a bundle marked with'use client'
, separate from any bundle containing exports that can be used directly on the server.
Many components in @carbon/react
contain non-serializable props (functions, dom elements, date objects). In this case it sounds like we wouldn't want to assume use client
as these can not be serialized.
I'm very inexperienced with the carbon design system and what I'm gonna say might be partial or wrong. I think some of the components that carbon offers right now are not that far from being RSC-compatible.
Most of the basic components related to forms, like Form
or FormGroup
, or basic layout components like FlexGrid
cannot be considered server-side components because they rely on the usePrefix
hook to compose the final class attribute:
export default function Form({ className, children, ...other }: FormProps) {
const prefix = usePrefix();
const classNames = classnames(`${prefix}--form`, className);
return (
<form className={classNames} {...other}>
{children}
</form>
);
}
This split the broader topic into two sub-topics:
cds
value (or the preferred one). They'll lose the ability to have a context with the prefix, but I think would not come as a big loss. Ironically, it's closer to using vanilla carbon than the React version.Generally speaking, I think having the possibility to define basic pages with standard HTML components with links, button, forms without having to pass a lot of javascript to the client is a nice feature that RSC enables for React users, and Carbon should start thinking into that with the goal of reducing client size and supporting React's attempt to progressively embrace standard HTML (as a React developer, it must be like the first time in 5 years that I use a real HTML form).
By supporting an RSC version of Link
, Button
, Form
and related, Grid
and related, Tile
... and other components that offer limited interactions, it should be possible to create complete working websites that are server-side rendered only, like recreating the basic form-action example in this famous video.
It might work with making it a (server) context instead of a hook. Would also prefer that for components that are stateless.
@dumaron btw instead of fork a small babel function to replace usePrexix with the cds literal might be easier to work with than maintaining a fork
Hope we get upgrade soon for Nextjs 14
bump as RCS has been released for a while now.
We are having to wrap all components in a use client
:
'use client';
export * from '@carbon/react';
then import from this local file location instead.
Converting components to be static (where possible) improves the app performance and DX when using RSC. Nearly every component is incompatible because of the use of contexts-- particularly usePrefix
.
A start could be to migrate usePrefix
to a global variable instead.
// prefix.js
let prefix = 'cds';
export function setPrefix(newPrefix) {
prefix = newPrefix;
}
export function getPrefix() {
return prefix;
}
// Component.js
import { getPrefix } from './prefix.js'
export function Component() {
const prefix = getPrefix();
}
Then consumers import the setPrefix into their app.
import { setPrefix } from '@carbon/react';
export default function RootLayout({ children }) {
setPrefix('custom');
return (
<html lang="en">
<body>{children}</body>
</html>
);
}
When adding use client
to the components we'll need to be sure that it's applied correctly and not stripped by rollup https://github.com/rollup/rollup/issues/4699
When using a global/literal instead of context how would we solve for users being able to redefine the prefix for only a portion of their react tree? This is important for extending zoned/inline theming, versioning styles, and avoiding css conflicts in environments where styles are not well isolated between microservices. Removing support for this would be a breaking change.
I'd also be concerned about race conditions stemming from react not being able to trigger re-renders if the value changes. In concurrent rendering components might render multiple times before committing to the DOM, etc.
This is just one of the many system-wide issues we'd need to resolve. Many components rely on context for cross-component communication for features like focus management and may not work at all as a server component. Event handlers are everywhere, useId
is required for unique ids for accessibility. Nearly all components use forwardRef for flexibility. There's many usages of other DOM APIs. It's a complex array of things to address, which I think is why there hasn't been any movement on this yet.
The problem
To benefit from the new full-stack Web applications architecture of React 18 / NextJS App Router, we need the presentational CDS components to be React Server Components.
The solution
Avoid using client-side functions (React
use...
, events, ...) in presentational components.Examples
No response
Application/PAL
No response
Business priority
None
Available extra resources
No response
Code of Conduct