Skip to content
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

React 18: Expose useFlushEffect hook for 3rd party CSS-in-JS libraries #30997

Closed
devknoll opened this issue Nov 4, 2021 · 1 comment · Fixed by #34117
Closed

React 18: Expose useFlushEffect hook for 3rd party CSS-in-JS libraries #30997

devknoll opened this issue Nov 4, 2021 · 1 comment · Fixed by #34117
Assignees

Comments

@devknoll
Copy link
Contributor

devknoll commented Nov 4, 2021

The official guidance for CSS-in-JS libraries with React 18 streaming is to incrementally insert styles in the stream at the right time. This isn't something that is exposed directly to users in frameworks like Next.js though.

To resolve that, we'll support a useFlushEffect hook:

  • This hook is called right before writing content generated by React
  • The hook can only be used by the Document component exported by pages/_document

Together with render prop support, this completes the picture for support for 3rd party CSS-in-JS libraries with React 18 + Suspense SSR and streaming. Documentation follows below.

Note: The hook is limited to Document in pages/_document because it only makes sense to flush additional arbitrary HTML during the initial SSR. On navigation, the client will generate them at runtime. In the future, we may wish to SSR content during navigation with Server Components, but Server Components may need affordances for that, and we'll likely want a more specific hook (such as useStylesheet) too.


Next.js provides support that CSS-in-JS libraries can leverage to streamline integration with applications. By convention, libraries should publish a utility (such as next-styled-jsx) that returns a flush effect and render prop the application can use:

The flush effect is called by Next.js before flushing HTML content rendered by React. The handler can return additional HTML or JSX that should be inserted before that content. The render prop should wrap the application to provide any necessary context for e.g. storing generated styles.

For example, the library code might look something like this:

export function useStyledJsx() {
  const registry = createStyleRegistry()
  const flushEffect = () => {
    const styles = registry.styles()
    registry.flush()
    return styles
  }
  const withStyledJsx = children => (
    <StyleRegistry registry={registry}>
        {children}
    </StyleRegistry>
  )
  return [flushEffect, withStyledJsx]
}

By convention, applications will use the utility like so:

import {
  useFlushEffect,
  Html,
  Head,
  Main,
  NextScript,
} from 'next/document'

import { useStyledJsx } from 'next-styled-jsx'

export default function Document() {
  const [flushEffect, withStyledJsx] = useStyledJsx()
  useFlushEffect(flushEffect)
  return (
    <Html>
      <Head />
      <body>
        <Main>{children => withStyledJsx(children)}</Main>
        <NextScript />
      </body>
    </Html>
  )
}

Note: to avoid taking a dependency on Next.js, the useFlushEffect hook should only be added by applications, not libraries.

@devknoll devknoll self-assigned this Nov 4, 2021
@devknoll devknoll changed the title Expose unstable_useFlush hook for 3rd party CSS-in-JS libraries to use with React 18 React 18: Expose unstable_useFlush hook for 3rd party CSS-in-JS libraries Nov 5, 2021
@devknoll devknoll changed the title React 18: Expose unstable_useFlush hook for 3rd party CSS-in-JS libraries React 18: Expose useFlushEffect hook for 3rd party CSS-in-JS libraries Nov 9, 2021
@kodiakhq kodiakhq bot closed this as completed in #34117 Feb 18, 2022
kodiakhq bot pushed a commit that referenced this issue Feb 18, 2022
Implements #30997 with some minor tweaks to the design:

* The hook is moved to Client Components (e.g. `pages/_app` instead of `pages/_document`). This was a silly oversight in the original design: the hook needs to be called during server prerendering.

* `useFlushEffects` instead of `useFlushEffect` as there isn't a particularly safe way to implement the singular semantics as a Client Component hook given the current implementation of server rendering.

---

Fixes #30997
@github-actions
Copy link
Contributor

This closed issue has been automatically locked because it had no new activity for a month. If you are running into a similar issue, please create a new issue with the steps to reproduce. Thank you.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Mar 20, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants