Open lunelson opened 4 years ago
Hi,
This is definitively something we should have, but is not so easy. We also need to extract critical CSS to inline it in the pages.
It's already been mentioned somewhere that we may need to do something similar to how Gatsby works (docusaurus-browser/docusaurus-ssr files...).
I don't have much more infos to provide, this needs exploration :)
Maybe a good start would be to study all the css-in-js gatsby plugins, and maybe create some kind of doc/spreadsheet, to see what are the useful lifecycles / api surface to provide to make it possible to support a wide range of CSS-in-JS libraries.
Yes, off the top of my head the necessary operations for CSS-in-JS libs tend to be:
<App />
component with custom Providers which enable collection and critical extraction<head/>
elements with those extracted styles, per pageastroturf
and linaria
: extend the webpack config with custom loadersThe latter 3 are all supported in Docusaurus in one way or another AFAICT; what's really missing is wrapping the <App />
element.
In Gatsby as I'm sure you know, this is done through the wrapRootElement
and/or wrapBodyElement
functions, which are exported from gatsby-ssr.js
and gatsby-browser.js
.
It seems to me, that these correspond to the serverEntry.js
and clientEntry.ts
files I mentioned above... so I'm thinking some plugin APIs like wrapServerRootElement
and wrapClientRootElement
, as long as they could also provide a way to set Head
elements, would do the trick
Examples from Gatsby—note: many only require a babel plugin for the client side, and only modify rendering for the SSR part
@slorber on closer examination, after trying to hack this today: I'm guessing when you said "it's not so easy" you might have been thinking about react-loadable
...? This scheme doesn't seem so far to be compatible with the HOCs that are needed for collecting CSS from the component tree
Thanks for the details @lunelson
I don't have time to investigate these things right now but we'll likely come back to it once i18n is there
Note, we might introduce a way to wrap root element in this PR: https://github.com/facebook/docusaurus/pull/3153
@slorber yes functions for wrapping root and/or page element would also solve https://github.com/facebook/docusaurus/issues/2891. I see that you've indicated it as low-priority, but IMO it's rather important, e.g. for components or context-providers that may be maintaining global state and doing async operations, such as an analytics consent-manager which is causing me trouble at the moment
@lunelson in the meantime alpha66 declare a new component "LayoutProviders" that you can swizzle and wrap with custom providers.
For minimum maintenance burden, you can also check this doc to "enhance" an existing theme comp without duplicating its code: https://v2.docusaurus.io/docs/using-themes/#wrapping-theme-components
But I understand the issue about stateful providers resetting their states. We'll try to solve this soon after I finish i18n.
Thanks for the tips @slorber, good to know about those new components, but indeed yes the remounting-on-route-change problem remains open
@slorber @lunelson Could you suggest any workaround to avoid a flash of unstyled content using docusaurus and styled-components
(or any other CSS-in-JS lib as far as I understand)?
I would be happy to help implementing a real solution if you could suggest some direction.
An example of the problem I am facing with the code here (you might need to simulate a slower network to experience that)
@sawasawasawa yes I believe this is the essential problem with CSS-in-JS in docusaurus, that is the lack of server-side-rendering support. Normally in the SSR phase, CSS-in-JS rules should be collected and written in to the <head>
of the document, via functions that wrap the <body>
. AFAIK we don't have an API yet in docusaurus to enable this. The result is that styles are only created on the client side after hydration, hence the FOUC
So the fouc has something to do with docusaurus and not with styled components? https://stackoverflow.com/questions/66212466/css-styles-get-applied-with-1s-delay-styled-components?noredirect=1#comment117063613_66212466
It has something to do with both. If you use server-side rendering like Docusaurus, Gatsby or Next, a CSS-in-JS like Styled-Components need proper integration with the framework to ensure critical CSS is inlined in the HTML files and avoid FOUC. It is documented on SC here: https://styled-components.com/docs/advanced#server-side-rendering
ok.. so the issue can only be fixed in docusaurus itself? I will take a look into this.. Wanted to find out a bit more about server side rendering anyway ^^
ok.. I investigated some time in fixing this issue.. but I couldn't figure out how to implement such thing in docusaurus..
so is there a plan in creating a styled-component integration for docusaurus in the near future or should I rather switch to css modules :/
@bennodev19 the solution requires an API to be made available from docusaurus, similar to Gatsby's wrapRootElement
and wrapPageElement
APIs, or Next.js' _document.js
and _app.js
APIs, that enable intervention at the root of the application to provide as well as collect data. These APIs don't exist (yet)
yeah sure.. I tried to create such interface/api.. but I wasn't able to bring it to life.. so I wanted to know if other people (with more knowledge in this area) have planned to invest some time in creating such api ^^ because if not I see no reason to stick to styled components for this project (agile-docs), since I want to launch/update the landing page with proper styling in the near future. And that isn't possible with styled-components yet because also theme props are undefined.. (which of course makes sense)
@bennodev19 that's something we want to work on but is not so simple. APIs are forever so we'd rather find a good abstraction for CSS-in-JS integration that will work for all the libs, not just SC. We could copy Gatsby's abstraction but it's also a good time to study what has worked well and not so well for Gatsby by studying all its CSS-in-JS plugins a bit.
You'd rather convert to css modules if you cannot wait because I don't think we should rush on implementation without a careful design.
Hey guys,
I found a super hacky way to collect styles for styled-components
, and I do not recommend it to anyone, but it works :). Most likely, it breaks hydration and forcing react to re-render the whole tree. Sadly, I don't know how to force the non-production build to check, but honestly, this is much better than FOUC.
I'm voting for public API, which will allow returning your element before passing it to renderToString
from react-dom/server
.
UPD: Fixed DocusaurusContext
imports
UPD (2022-05-06): No longer works for v2.0.0-beta.16+
UPD (2022-06-10): Fixed for v2.0.0-beta.21+ thanks to @EmaSuriano!
I think that we can add a plugin point here: https://github.com/facebook/docusaurus/blob/main/packages/docusaurus/src/client/serverEntry.tsx#L88 It basically renders whole app so we can add a plugin lifecycle method that can customize somehow this method, and in case of styled components the plugin should do sth like this:
plugin.serverSideRender = (renderToString, element /*wholeTreeWithLoadableHelmetProviderStaticRouterEtc*/)=>{
const sheet = new ServerStyleSheet();
const html = renderToString(sheet.collectStyles(element))
const headTags = sheet.getStyleTags()
return {headTags, html}
}
Yes, that seems like a reasonable thing to do.
I'd be interested if someone could help design an API that works for most CSS-in-JS libs, and also allow composition (ie one plugin provides its CSS-in-JS integration, and you can use 2 CSS-in-JS libs by combining 2 plugins)
This requires studying the ecosystem a bit.
I'd like to have a table with expected CSS-in-JS libs compatibility before merging a PR.
This is the Gatsby API: https://www.gatsbyjs.com/docs/reference/config-files/gatsby-ssr/#replaceRenderer
en...
I can use styled-components
I use "babel-plugin-styled-components",
and then, use
I would recommend considering https://github.com/callstack/linaria as it is much better for performance than others.
hello Docusaurus friends 👋
I wanted to report having the same issue here w/ rendering any styled-components in my extended/swizzled Docusaurus templates. This caused a good deal of flicker and in some cases the CSS did not load initially at all. As of now, the workaround @exah describes above is working well for me when I render these components separately during the Layout rendering when the window is not yet available. Some Docusuarus components are SSR safe, but others which rely on Scroll Context or Announcement Context etc do not seem to be at the moment. Just sharing for anyone else facing this challenge.
(swizzled theme-common Layout):
import { Header } from './src/Header'; // custom header
import { ThemeProvider } from '@mui/material/styles';
import ErrorBoundary from '@docusaurus/ErrorBoundary';
import Head from '@docusaurus/Head';
import { PageMetadata, ThemeClassNames } from '@docusaurus/theme-common';
import AnnouncementBar from '@theme/AnnouncementBar';
import ErrorPageContent from '@theme/ErrorPageContent';
import Footer from '@theme/Footer';
import LayoutProvider from '@theme/Layout/Provider';
import Navbar from '@theme/Navbar';
import SkipToContent from '@theme/SkipToContent';
import clsx from 'clsx';
import React from 'react';
import { ThemeProvider as SCThemeProvider } from 'styled-components';
import { getTheme } from './my-mui-theme/theme';
import ServerStyle from './ServerStyle'; // workaround described above from @exah
import styles from './styles.module.css';
import { useKeyboardNavigation } from './useKeyboardNavigation';
const themeProps = { theme: getTheme() };
export default function Layout(props: any) {
const {
children,
noFooter,
wrapperClassName,
// Not really layout-related, but kept for convenience/retro-compatibility
title,
description,
} = props;
useKeyboardNavigation();
const header = (
// ssr/styled-components header
<Header/>
);
if (typeof window === 'undefined') {
// extracts styles from server renderable content (e.g. contents w/ styled-components)
// for initial css load. Some Docusaurus contents are not currently SSR safe.
const ssrContent = (
<ThemeProvider {...themeProps}>
<SCThemeProvider theme={themeProps.theme}>
<PageMetadata title={title} description={description} />
{header}
<div className={clsx(ThemeClassNames.wrapper.main, styles.mainWrapper, wrapperClassName)} />
</SCThemeProvider>
</ThemeProvider>
);
return <ServerStyle from={ssrContent} />;
}
return (
<LayoutProvider>
<ThemeProvider {...themeProps}>
<SCThemeProvider theme={themeProps.theme}>
<PageMetadata title={title} description={description} />
<SkipToContent />
<AnnouncementBar />
{header}
<div className={clsx(ThemeClassNames.wrapper.main, styles.mainWrapper, wrapperClassName)}>
<ErrorBoundary fallback={(params) => <ErrorPageContent {...params} />}>{children}</ErrorBoundary>
</div>
<Footer />
</SCThemeProvider>
</ThemeProvider>
</LayoutProvider>
);
}
I'm commenting on this without thinking - we can use https://stitches.dev
The usage of React Server Components is growing, and we plan to adopt them in Docusaurus.
And we see that the past generation of runtime-based CSS-in-JS libs does not play with React Server Components (more explanations here: https://github.com/facebook/docusaurus/discussions/8959).
At this point I'm not even sure we need to have these hooks anymore, it goes against the industry trend to adopt no-runtime solutions.
Many existing libs are in the process of figuring out how to benefit most from Server Components, so I think we should wait and decide later what is best for Docusaurus.
For the moment, I'm leaning towards not implementing these hooks because I believe it wouldn't encourage the usage of performant code by default in the long term, and the only good reason to implement those is to be compatible with legacy UI libs.
💥 Proposal: Hooks for CSS-in-JS Client and Server Rendering
So, the [v2] ☂️ Umbrella issue for v2 docs has the above open TODO (although the issue has been closed); and this is also currently mentioned in the docs as "welcoming PRs".
I have some experience with the setup patterns for
emotion
(incl. v11) andstyled-components
here, and would like to help with this. As far as I can tell from the source, this would require additions to the Lifecycle APIs so that theserverEntry.js
andclientEntry.ts
files' rendering functions could be modified. I would need some guidance on creating these hooks.Anyone want to help me get started?