kriasoft / isomorphic-style-loader

CSS style loader for Webpack that is optimized for isomorphic (universal) web apps.
https://reactstarter.com
MIT License
1.27k stars 143 forks source link

Support for next.js #204

Open ajayjaggi97 opened 2 years ago

ajayjaggi97 commented 2 years ago

Can you pls share the insights of using isomorphic-style-loader with next.js. How can we pass the required context value of insertCss??

Ainias commented 2 years ago

@ajayjaggi97 I've tried for the last 3 days to get it to work with next.js and I'm finally happy to use it. First of all: I want to use it for a component-library and not directly in next.js. I think for direct usage, next.js already has a nice system through the *.module.scss/css-files.

This is where the magic happens: Here is the content of my _app.tsx:

export default function MyApp({ }: AppProps) {
    const styles: string[] = useMemo(() => [], []);

    const addStyles = useCallback(
        (...newStyles) => {
            if (typeof document === 'undefined') {
                styles.push(...newStyles.map((s) => s._getCss()));
            } else {
                newStyles.forEach((s) => s._insertCss());
            }
        },
        [styles]
    );

    return (
        <>
            <StyleProvider value={{ insertCss: addStyles }}>
                <ColorInput />
            </StyleProvider>
            <StyleRenderer styles={styles} />
        </>
    );
}

and my StyleRenderer.tsx:

export type StyleRendererProps = { styles: string[] };

export function StyleRenderer({ styles }: StyleRendererProps) {
    return (
        <Head>
            <style>{styles.join()}</style>
        </Head>
    );
}

Note that I use useMemo in order to save the styles and StyleRenderer, which only displays the styles. This is because of how nextjs works. Once the rendermethod is done, you can't trigger an rerender on the server, regardless what you do (or at least I could not trigger one). But subcomponents are still rendering. Since the components are called top-down, the ColorInput is called before the StyleRenderer. Inside the ColorInput I use the useStyles hook, which calls addStyles from _app.tsx. Since I used useMemo for styles, it is the same instance inside addStyles as is passed to StyleRenderer, Since StyleRenderer renders last, it will render all the styles needed inside the server rendering. On the client, I just use the s._insertCss() method without any problems.

And also note that I did not use StyleContext.Provider but StyleProvier which is an reexport of StyleContext.Provider from my component-library. This is needed or else the context used from useStyles inside the library is a different one than the one provides through StyleContext.Provider inside next.js

ajayjaggi97 commented 2 years ago

Hey @Ainias .Thanks for the solution. I had the similar use case where we had a components library.

tagerwang commented 1 year ago

Do you have a specific example for reference? thank you

childrentime commented 9 months ago

@Ainias @ajayjaggi97 can you give a example? I'm very curious about how to actually implement this🤪.