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 npm packages using rollup not working with srr or nextjs? #438

Open
siamahnaf opened this issue Dec 19, 2022 · 5 comments
Open

React npm packages using rollup not working with srr or nextjs? #438

siamahnaf opened this issue Dec 19, 2022 · 5 comments

Comments

@siamahnaf
Copy link

I create a simple react packages for personal uses (private package). But when I want to use it into nextjs project it's css not working with ssr. Actually, working but not ssr friendly. Here after component loading then css will be loaded. But I want it with ssr. I mean css also work with ssr.

rollup.config.mjs-

import resolve from "@rollup/plugin-node-resolve";
import commonjs from "@rollup/plugin-commonjs";
import typescript from "@rollup/plugin-typescript";
import dts from "rollup-plugin-dts";
import postcss from "rollup-plugin-postcss";
import peerDepsExternal from "rollup-plugin-peer-deps-external";

export default [
    {
        input: "src/index.ts",
        output: [
            {
                file: "dist/cjs/index.js",
                format: "cjs",
                sourcemap: true,
            },
            {
                file: "dist/esm/index.js",
                format: "esm",
                sourcemap: true,
            },
        ],
        plugins: [
            peerDepsExternal(),
            resolve(),
            commonjs(),
            typescript({ tsconfig: "./tsconfig.json" }),
            postcss()
        ],
        external: ["react", "react-dom"]
    },
    {
        input: "dist/esm/types/index.d.ts",
        output: [{ file: "dist/index.d.ts", format: "esm" }],
        plugins: [dts()],
        external: [/\.css$/]
    }
];

Can anyone help me.

@alfredosalzillo
Copy link

alfredosalzillo commented May 7, 2023

@siamahnaf I have found a solution.

In the library:

// src/style-inject.js
export default function styleInject(css, id, { insertAt } = {}) {
  if (!css) return
  if (typeof document === 'undefined') {
    globalThis.ssrCss = globalThis.ssrCss || []
    globalThis.ssrCss.push({ css, id })

    return
  }

  if (document.getElementById(id)) return

  const head = document.head || document.getElementsByTagName('head')[0]
  const style = document.createElement('style')
  style.id = id
  style.type = 'text/css'

  if (insertAt === 'top') {
    if (head.firstChild) {
      head.insertBefore(style, head.firstChild)
    } else {
      head.appendChild(style)
    }
  } else {
    head.appendChild(style)
  }

  if (style.styleSheet) {
    style.styleSheet.cssText = css
  } else {
    style.appendChild(document.createTextNode(css))
  }
}
// rollup.config.js
import { randomUUID } from 'crypto';
import path from 'path';

const styleInjectPath = path
  .resolve('./src/style-inject.js')
  .replace(/[\\/]+/g, '/')

const ids = new Map()
const getUniqueId = (id) => {
  if (ids.has(id)) return ids.get(id)
  const uid = randomUUID()
  ids.set(id, uid)

  return uid
}

...
postcss({
      inject(cssVariableName, id) {
        return `
          import styleInject from '${styleInjectPath}';
          styleInject(${cssVariableName}, 'style-${getUniqueId(id)}');
        `
      },
    }),
...

In the Next js project

// next pages/_document.js
import React from 'react'

type SSRCssModule = {
  css: string
  id: string
}
interface GlobalThis {
  ssrCss?: SSRCssModule[]
}

declare const globalThis: GlobalThis

const SSRInjectStyles: React.FC = () => {
  if (!globalThis.ssrCss) return null

  return (
    <>
      {globalThis.ssrCss.map((module: { css: string; id: string }) => (
        <style
          dangerouslySetInnerHTML={{
            __html: module.css,
          }}
          id={module.id}
          key={module.id}></style>
      ))}
    </>
  )
}


export default Document(props) {
  return (
    ...
  <Head>
  ...
  <SSRInjectStyles />
  </Head>
    ...
  )
}

@adarsh-drishya
Copy link

where is globalThis coming from @alfredosalzillo in the first file it is not defined

@cristiannietodev91
Copy link

your solution works @alfredosalzillo. I put the SSRInjectStyles component within my own library to allow consumers to import it.

and I found that the styles were inserted multiple times. I had to change a little bit your styleInject script by next one.

export default function styleInject(css, id, { insertAt } = {}) {
	if (!css) return;
	if (typeof document === "undefined") {
		globalThis.ssrCss = globalThis.ssrCss || [];
		const keys = globalThis.ssrCss.reduce((acc, curr)=> {
			return {
				...acc,
				[curr.id]: {
					...curr
				}
			};
		},{});
		if(!keys[id]){
			globalThis.ssrCss.push({ css, id });
		}
		return;
	}

	if (document.getElementById(id)) return;

	const head = document.head || document.getElementsByTagName("head")[0];
	const style = document.createElement("style");
	style.id = id;
	style.type = "text/css";

	if (insertAt === "top") {
		if (head.firstChild) {
			head.insertBefore(style, head.firstChild);
		} else {
			head.appendChild(style);
		}
	} else {
		head.appendChild(style);
	}

	if (style.styleSheet) {
		style.styleSheet.cssText = css;
	} else {
		style.appendChild(document.createTextNode(css));
	}
}

@alfredosalzillo
Copy link

@adarsh-drishya wich bundler are you using?

@alfredosalzillo
Copy link

@cristiannietodev91 the bundler and the import cache of modules should have prevented the duplication when a component is used multiple time during the SSR, but nice catch :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants