next.jssassvitecss-modulesturborepo

No style from turborepo "external" package component imported in NextJS page


I have a series of small atomic packages that I want to be able to publishing and versioning independently, all within a monorepo using TurboRepo.

When these components were used as "internal" components, there was no issue.

However, when I tried to perform builds, I discovered that I couldn't get any CSS styles applied to these components when they were imported and used in my Next.js application (/apps/web).

For example, the Button package contains a single file named Button.tsx and a file named Button.module.scss.

import styles from './Button.module.scss';

const Button = forwardRef<HTMLButtonElement, ButtonProps>(
  (
    {
      className,
      ...restProps
    },
    ref
  ) => {
    return (
      <button className={styles.base} ref={ref} {...restProps}>
         ...
      </button>
    );
  }
);

My vite.config.js looks like :

import path from 'path';
import react from '@vitejs/plugin-react';
import { defineConfig } from 'vite';
import dts from 'vite-plugin-dts';

export default defineConfig({
  build: {
    minify: 'terser',
    lib: {
      formats: ['es', 'cjs'],
      entry: path.resolve(__dirname, 'src/index.ts'),
      name: 'Button',
      fileName: format => `button.${format}.js`,
    },
    rollupOptions: {
      // externalize deps that shouldn't be bundled
      // into your library
      external: ['react', 'react-dom'],
      output: {
        exports: 'named',
        sourcemap: true,
        format: 'es',
        preserveModules: true,
      },
      treeshake: true,
    },
  },
  plugins: [
    react(),
    dts({
      copyDtsFiles: true,
      exclude: ['**/node_modules'],
      insertTypesEntry: true,
    }),
  ],
});

The package.json :

  "name": "@company/components__button",
  "version": "0.0.1",
  "types": "./dist/index.d.ts",
  "exports": {
    ".": {
      "import": "./dist/button.es.js",
      "require": "./dist/button.cjs.js"
    }
  },
  "files": ["dist/**"],

Here is a partial view of the output dist files.

Then, I import the package in my Next app (apps/web) like this :

"@company/components__button": "workspace:*",

Now, I can import it, but** no style is applied** at runtime !

However, If I inspect the DOM, I can see the class on the button DOM node:

<button class="_base_3rp5a_4" type="submit">create an account</button>

Upon inspecting the network tab, there is a GET request fetching the page's CSS. However, the payload only contains the CSS defined at the Next.js application level and used on this same page but none of the styles included in the imported package...

Shouldn't it also include the CSS of the button imported from my package?

Does Next.js is struggling to reconcile dynamic classes with their associated styles at runtime. Am I approaching this incorrectly? Have I missed something in the configuration?

I would like the style associated with the imported package to be applied correctly.

I followed this guide : https://turbo.build/repo/docs/handbook/publishing-packages/bundling

Edit finale solution :

  1. First, the components packages are built, and CSS files are generated within their dist folder.
  2. Only after that, the css package builds its own index.css file and removes styles.css from the components packages. Each component package operates autonomously. The removal of style files occurs only in context of monorepo (They are useless in the final build).

Finally, the reconciliation process proceeds seamlessly, the style is successfully fetched on the browser side, and consequently, becomes active.

You can find an example of the finale solution here.


Solution

  • Since you are bundling your UI library through esbuild, there shall be couple of files generated by esbuild in dist directory.

    1. index.js
    2. index.js.map (only if you enable sourcemap)
    3. style.css (this is very important)

    The issue here is that, you need to import that style.css in the Next.js app which uses the above UI library by,

    import '<path to the style.css>';
    

    Which is why even though you have the CSS classes in your HTML elements (from the index.js part of your UI library), no styles are applied (because those CSS classes don't exist in your DOM)