I am trying to build a component library for a new nextJS application. The nextJS app has the "app router".
Styled-components is working fine when I use it, integrated directly in the nextJS code, the issue arrises when I want to use the component library, I have on the side.
I did everything recommended by nextJS, I set the following in my next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
compiler: {
styledComponents: {
// Enabled by default.
cssProp: true,
},
},
}
module.exports = nextConfig
I also included the "StyledComponentsRegistry" as per the nextJS definition.
I have configured the library, such that it is build using rollup.
First I tried keeping styled-components as a peerDependency, and added it as "external" in rollup. That did not work(It worked fine in the browser, since the client code works fine, but it gave me an error in the console, when generating the page. It basically gave me the following error, as webpack was not able to include the styled-components library styled_components__webpack_imported_module_0__ is not a function
)
So instead I tried including styled-components as a direct dependency, and I modified the StyledComponentsRegistry, to also include the library's version of styled-components. Like the following:
'use client';
import React, { useState } from 'react';
import { useServerInsertedHTML } from 'next/navigation';
import { ServerStyleSheet, StyleSheetManager } from 'styled-components';
import { StyledComponentsServerStyleSheet, StyledComponentsStyleSheetManager } from 'skafte-cms';
export default function StyledComponentsRegistry({
children,
}) {
// Only create stylesheet once with lazy initial state
// x-ref: https://reactjs.org/docs/hooks-reference.html#lazy-initial-state
const [styledComponentsStyleSheet] = useState(() => new ServerStyleSheet());
const [cmsStyledComponentsStyleSheet] = useState(() => new StyledComponentsServerStyleSheet());
useServerInsertedHTML(() => {
const styles = styledComponentsStyleSheet.getStyleElement();
styledComponentsStyleSheet.instance.clearTag();
const cmsStyles = cmsStyledComponentsStyleSheet.getStyleElement();
cmsStyledComponentsStyleSheet.instance.clearTag();
return <>{styles}{cmsStyles}</>;
});
if (typeof window !== 'undefined') return <>{children}</>;
return (
<StyleSheetManager sheet={styledComponentsStyleSheet.instance}>
<StyledComponentsStyleSheetManager sheet={cmsStyledComponentsStyleSheet.instance}>
{children}
</StyledComponentsStyleSheetManager>
</StyleSheetManager>
);
}
This did work, but when I load the page, I get an error in the console, telling me the styled-component is not being hydrated properly(It only maintains it's class that is based on the file-location, not the short class-identifier responsible for the styling)
Any help is appreciated
To fully use styled-components with NextJs you need:
module.exports = {
compiler: {
styledComponents: {
// Enable display of the component name along with the generated className (needed for debugging).
displayName: true,
// Enable SSR support
ssr: true,
// Optional
fileName: false,
},
},
}
// babel.config.js
module.exports = {
plugins: [
[
"babel-plugin-styled-components",
{
displayName: true,
fileName: false,
ssr: true,
},
],
],
};
styled-components
package in your library as peerDependency (yarn add styled-components --peer
).rollup-plugin-peer-deps-external
plugin, which will automatically exclude from the bundle the dependencies designated as peerDependency in package.json of your library.babel.config.js
file in the root of the library with content similar to what was demonstrated above.babel-plugin-styled-components
plugin will make the necessary changes.Using the babel-plugin-styled-components plugin for the library is a must, because it will add the withConfig function with the settings needed for easy debugging during development and SSR support so there are no hydration problems.
For example (output bundle):
var StyledView = styled.div.withConfig({
displayName: "StyledView",
componentId: "sc-10aexlr-0"
})({
padding: 10,
border: "1px solid teal"
});