React npm packages using rollup not working with srr or nextjs?
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.
@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>
...
)
}
where is globalThis coming from @alfredosalzillo in the first file it is not defined
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));
}
}
@adarsh-drishya wich bundler are you using?
@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 :)