Search code examples
reactjstypescriptwebpacknext.jsstyled-components

Next.js `transpilePackages` giving Webpack error with Styled Components


I'm trying to use components in a repository that are imported from a custom UI library. Both use the same stack (React, Typescript, Styled Components) with the web app using Next.js. When running npm run dev I'm getting this error:

 ⨯ TypeError: styled_components__WEBPACK_IMPORTED_MODULE_1__ is not a function
    at eval (webpack-internal:///./node_modules/@test/test-ui/dist/components/CtaButton/CtaButton.style.js:35:22)
    at ./node_modules/@test/test-ui/dist/components/CtaButton/CtaButton.style.js (C:\Users\Josh\Everything\Files\projects\work\test\test\.next\server\vendor-chunks\@test.js:90:1)
    at __webpack_require__ (C:\Users\Josh\Everything\Files\projects\work\test\test\.next\server\webpack-runtime.js:33:42)
    at eval (webpack-internal:///./node_modules/@test/test-ui/dist/components/CtaButton/CtaButton.jsx:7:74) 
    at ./node_modules/@test/test-ui/dist/components/CtaButton/CtaButton.jsx (C:\Users\Josh\Everything\Files\projects\work\test\test\.next\server\vendor-chunks\@test.js:20:1)
    at __webpack_require__ (C:\Users\Josh\Everything\Files\projects\work\test\test\.next\server\webpack-runtime.js:33:42)
    at eval (webpack-internal:///./node_modules/@test/test-ui/dist/components/CtaButton/index.js:2:68)      
    at ./node_modules/@test/test-ui/dist/components/CtaButton/index.js (C:\Users\Josh\Everything\Files\projects\work\test\test\.next\server\vendor-chunks\@test.js:100:1)
    at __webpack_require__ (C:\Users\Josh\Everything\Files\projects\work\test\test\.next\server\webpack-runtime.js:33:42)
    at eval (webpack-internal:///./node_modules/@test/test-ui/dist/components/index.js:2:68)
    at ./node_modules/@test/test-ui/dist/components/index.js (C:\Users\Josh\Everything\Files\projects\work\test\test\.next\server\vendor-chunks\@test.js:170:1)
    at __webpack_require__ (C:\Users\Josh\Everything\Files\projects\work\test\test\.next\server\webpack-runtime.js:33:42)
    at eval (webpack-internal:///./node_modules/@test/test-ui/dist/index.js:2:69)
    at ./node_modules/@test/test-ui/dist/index.js (C:\Users\Josh\Everything\Files\projects\work\test\test\.next\server\vendor-chunks\@test.js:240:1)
    at __webpack_require__ (C:\Users\Josh\Everything\Files\projects\work\test\test\.next\server\webpack-runtime.js:33:42) {
  page: '/'
}

And my Next config:

const nextConfig = {
  transpilePackages: ["@test/test-ui"],
  output: "export",
  reactStrictMode: true,
  compiler: {
    styledComponents: true,
  },
};

export default nextConfig;

To distribute the UI library I'm using the following command:

rm -rf dist && npx tsc && npx tsc-alias

Here's the TS Config:

{
  "compilerOptions": {
    "lib": ["dom", "dom.iterable", "esnext"],
    "target": "ES2020",
    "allowJs": false,
    "skipLibCheck": true,
    "strict": true,
    "noImplicitAny": true,
    "noImplicitThis": true,
    "alwaysStrict": true,
    "strictBindCallApply": true,
    "strictNullChecks": true,
    "strictFunctionTypes": true,
    "strictPropertyInitialization": true,
    "forceConsistentCasingInFileNames": true,
    "esModuleInterop": true,
    "module": "esnext",
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "jsx": "preserve",
    "incremental": true,
    "declaration": true,
    "outDir": "dist",
    "sourceMap": true,
    "allowSyntheticDefaultImports": true,
    "baseUrl": "./",
    "paths": {
      "@/*": ["./src/*"]
    }
  },
  "include": ["src/**/*.ts", "src/**/*.tsx"],
  "exclude": [
    "node_modules",
    "src/**/*.test.ts",
    "src/**/*.test.tsx",
    "src/**/*.stories.ts",
    "src/**/*.stories.tsx"
  ]
}

Long story short, I'm just compiling the TS/TSX files into dist/, along with declaration files.

Seems like there's not really any documentation on this. Most of the solutions that I've seen have included Babel, however those seem outdated as Next.js moved its default compiler to SWC instead. Additionally there was previously a library used to transpile packages, however this is now integrated in the Next config.

What I know is that I get an error without the transpilePackages property set in the Next config:

 ⨯ ./node_modules/@test/test-ui/dist/components/CtaButton/CtaButton.jsx
Module parse failed: Unexpected token (4:12)
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this 
file. See https://webpack.js.org/concepts#loaders
| import { ButtonTheme } from "./types";
| export function CtaButton({ text, href, onClick, theme = ButtonTheme.Light, }) {
>     return (<CtaButtonBox href={href} onClick={onClick} $buttonTheme={theme}>
|       <ButtonText>{text}</ButtonText>
|       <ArrowSvg />

Import trace for requested module:
./node_modules/@test/test-ui/dist/components/CtaButton/CtaButton.jsx
./node_modules/@test/test-ui/dist/components/CtaButton/index.js
./node_modules/@test/test-ui/dist/components/index.js
./node_modules/@test/test-ui/dist/index.js

Adding the transpilePackages property solves it, which makes sense, so I'm confident that this is narrowed down to either my build process or a Webpack/Next config issue. I've tried using Babel, SWC, and Rollup all independently to resolve this issue and none of the configurations seem to do it. I have seen a Babel plugin that resolves some issues with Styled Components, but it didn't work for this particular issue


Solution

  • Of course, soon after posting this I found a solution. I found myself looking at the Next.js compiler documentation hoping I could glean something about a Webpack/SWC config option I could adjust, when I noticed a disclaimer in the Styled Components section:

    Note: minify, transpileTemplateLiterals and pure are not yet implemented. You can follow the progress here. ssr and displayName transforms are the main requirement for using styled-components in Next.js.

    I had not heard of the transpileTemplateLiterals option before, so I was immediately hopeful that would solve my issue. A quick try showed that to not be the case.

    I then visited the Github issue linked in that note and casually scrolled through the comments, not really seeing anything useful. There was a bit of chatter about a default export not working with Styled Components but it didn't seem too relevant. Turns out that it was the key, and the very last comment in that issue made me give it a try:

    Can confirm that import { styled } from 'styled-components'; is now also working as expected. (Tested Next.js 14.0.4-canary.41) Thank you @kdy1! 🙌

    Turns out, updating all of my imports in my UI library from import styled from "styled-components" to import { styled } from "styled-components" completely resolved the issue.