How to use styled-components with rollup and next.js - css not imported
Asked Answered
V

1

3

I have the components getting imported from my component library internal to my company.

The component library uses rollup:

rollup.config.mjs

import commonjs from '@rollup/plugin-commonjs';
import json from '@rollup/plugin-json';
import resolve from '@rollup/plugin-node-resolve';
import typescript from '@rollup/plugin-typescript';
import dts from 'rollup-plugin-dts';
import peerDepsExternalPlugin from 'rollup-plugin-peer-deps-external';
import postcss from 'rollup-plugin-postcss';
import { babel } from '@rollup/plugin-babel';
import { createRequire } from 'node:module';
const requireFile = createRequire(import.meta.url);
const packageJson = requireFile('./package.json');

export default [
  {
    input: 'src/index.ts',
    output: [
      {
        file: packageJson.main,
        format: 'cjs',
        sourcemap: true,
      },
      {
        file: packageJson.module,
        format: 'esm',
        sourcemap: true,
      },
    ],

    plugins: [
      babel({ babelHelpers: 'bundled' }),
      resolve(),
      commonjs(),
      typescript({
        exclude: ['**/tests/', '**/stories/'],
      }),
      json(),
      postcss({
        extensions: ['.css'],
      }),
      peerDepsExternalPlugin(),
    ],
  },
  {
    input: 'dist/index.d.ts',
    output: [{ file: 'dist/index.d.ts', format: 'es' }],
    plugins: [dts()],
    // not sure we want this (external) option here
    external: [/\.css$/]
  },
];

Then I build it.

And from the nextjs application I have it in the package json. use yarn link to link the two repos (and I know yarn link is working).

But none of the styles get passed through to my next js application using my component library.

I've tried changing my next.config.js to this and it did nothing.

/** @type {import('next').NextConfig} */
module.exports = {
  reactStrictMode: false,
  compiler: {
    styledComponents: {
      // Enabled by default.
      cssProp: true,
    },
  },
};

This is my .babelrc

{
  "plugins": ["babel-plugin-styled-components"]
}

Any help here would be incredible.

Viscometer answered 7/10, 2023 at 18:41 Comment(0)
R
3

This approach of keeping the CSS injection via style tags inline to the generated JavaScript does not work with Next.js since it generates has two compilations i.e. one for server and another for client-side code. Imagine you have a simple component in your library:

import React from 'react';

import style from './style.css';

export function MyComponent() {
  return (
    <div className={style.root}>
      <p>Hello world</p>
    </div>
  )
}

And, your CSS file is like this:

/* style.css */
.root {
  display: flex;
  font-size: 48px;
}

Then the bundle file generated by Rollup will be:

import React from 'react';

// Very simplified version.
function styleInject(css) {

  var head = document.head || document.getElementsByTagName('head')[0];
  var style = document.createElement('style');
  
  style.styleSheet.cssText = css;

  head.appendChild(style);
}

// Side effects (These won't be included in the client-side bundle)
var css_248z = ".root {\n  display: flex;\n  font-size: 48px;\n}\n";
styleInject(css_248z);

function MyComponent() {
  return React.createElement(
    "div",
    { className: css_248z.root },
    React.createElement("p", null, "Hello world")
  );
}

export { MyComponent };

The problem here is that Next.js won't include the styleInject function in the client-side bundle and running this code on server-side is meaningless.

Thus the only way is to actually generate/emit a standalone CSS file for your library and then include that in your Next.js app. Change the rollup-plugin-postcss configuration like this:

postcss({
  // Extract the CSS into a standalone file.
  extract: true,
  // You will have to use CSS Modules.
  modules: true,
  extensions: ['.css'],
}),

This will generated index.css file:

/* Name mangled */
.style_root__WnIqm {
  display: flex;
  font-size: 48px;
}

And, the bundled JS code will simply refer to this CSS class leaving the application developer to ensure that the CSS in properly injected:

// Bundled JS File by the Rollup after extraction
import React from 'react';

var style = { "root": "style_root__WnIqm" };

function MyComponent() {
  return React.createElement(
    "div",
    { className: style.root },
    React.createElement("p", null, "Hello world")
  );
}

export { MyComponent };

Finally, in your Next.js application layout file src/app/layout.jsx or tsx file, you can import this CSS:

import './globals.css';

// IMPORTANT: Library import CSS
import 'my-awesome-library/index.css';

export const metadata = {
  title: 'Create Next App',
  description: 'Generated by create next app',
}

export default function RootLayout({ children }) {
  return (
    <html lang="en">
      <body>{children}</body>
    </html>
  );
}

Reata answered 9/10, 2023 at 11:27 Comment(3)
Could you share a repository with this working build?Hordein
@Mathiasfc, I can take some time. I will see if I can do that over the weekend!Reata
I would also be interested in a working repositoryTrite

© 2022 - 2024 — McMap. All rights reserved.