styled-components / babel-plugin-styled-components

Improve the debugging experience and add server-side rendering support to styled-components
MIT License
1.08k stars 142 forks source link

How use with Vite.js, especially displayName for debugging. #350

Open valerii15298 opened 3 years ago

valerii15298 commented 3 years ago

When using with CRA, its possible to see name of component inside css classes, but I cannot figure out how to make a nice developing/debugging experience when using with Vite.js. Is there any advices how to show in classes names of components when using with Vite.js?

rockwotj commented 3 years ago

I suggest using https://www.npmjs.com/package/vite-plugin-babel-macros and importing from styled-components/macro. We have this working nicely in our vite setup (although that's with a custom plugin that should do the same as the package I pointed you too).

ronaldruzicka commented 3 years ago

Hi @rockwotj, please could you share your code how you set it up? Or could you please tell me what am I doing wrong?

I have this config

// vite.config.ts
import { defineConfig } from 'vite';
import reactRefresh from '@vitejs/plugin-react-refresh';
import macrosPlugin from "vite-plugin-babel-macros"

export default defineConfig({
  plugins: [
    reactRefresh(),
    macrosPlugin(),
  ],
});

And then I just import from styled-components/macros

// App.tsx

import styled from 'styled-components/macros';

But I get a Typescript error and error from Vite. I have the latest version of Styled Components: "styled-components": "^5.3.1"

// Typescript
Cannot find module 'styled-components/macros' or its corresponding type declarations.

// Vite
error when starting dev server:
Error: The following dependencies are imported but could not be resolved:
  styled-components/macros (imported by /Users/ronaldruzicka/Projects/vite-project/src/App.tsx)

Thanks!

rockwotj commented 3 years ago

Hi @rockwotj, please could you share your code how you set it up? Or could you please tell me what am I doing wrong?

Looks like it's not installed. Did you run npm install?

ronaldruzicka commented 3 years ago

Oh my bad, I was using plural /macros instead of singular /macro 🤦‍♂️ Now it's working properly, thanks a lot!

IvanBernatovic commented 3 years ago

@ronaldruzicka I added the macros plugin and started importing styled like this: import styled from 'styled-components/macro

But I still don't get proper class names, they are still randomized/minified. Is there anything else I should do?

rockwotj commented 3 years ago

@IvanBernatovic you need to have a config file to get display names, see the docs for more: https://styled-components.com/docs/tooling#experimental-config

ronaldruzicka commented 3 years ago

@IvanBernatovic I didn't use any other config. I just included that macrosPlugin in the vite.config.ts and then used import styled from 'styled-components/macro'; in the component and it worked.

I have these versions:

"styled-components": "^5.3.0"
"vite-plugin-babel-macros": "^1.0.5",

But maybe try that experimental config as well.

IvanBernatovic commented 3 years ago

@rockwotj @ronaldruzicka Thank you for your responses! I tried to add config as per styled-components docs but it still didn't work. I tried many things but in the end, solution was even simpler than this approach. This is what worked for me in vite.config.js:

// vite.config.js

export default defineConfig({
  plugins: [
    react({
      babel: {
        plugins: [
          [
            'babel-plugin-styled-components',
            {
              displayName: true,
              fileName: false
            }
          ]
        ]
      }
    })
  ]
})

No need to install any other plugin for babel macros (nor vite nor babel macros plugin) and no need to change exports to import styled from 'styled-components/macro'.

velsa commented 2 years ago

@IvanBernatovic I'm new to Vite. What additional config should I have to run the same plugin in a Typescript project? If I just add it as in your example, I get lots of errors from vite. It seems that it tries to parse all TS typecasts as react components :) E.g. it thinks that <ISomeInterface> is a component and complains.

IvanBernatovic commented 2 years ago

@IvanBernatovic I'm new to Vite. What additional config should I have to run the same plugin in a Typescript project? If I just add it as in your example, I get lots of errors from vite. It seems that it tries to parse all TS typecasts as react components :) E.g. it thinks that <ISomeInterface> is a component and complains.

Unfortunately, I can't help you with that as I don't have this setup (vite+typescript+styled-components) so I can't tinker around with config. Maybe if you had a reproducible example (a repo or codesandbox project) I could take a look.

Did you try other approaches from previous comments?

agriffis commented 2 years ago

E.g. it thinks that <ISomeInterface> is a component and complains.

@velsa That's a known peculiarity of tsx. For generics you can use <T extends unknown> (you don't need this if there are two or more params, only one). For casting you can use as. See https://github.com/Microsoft/TypeScript/issues/6897 and lots more if you google tsx generic cast

agriffis commented 2 years ago

@IvanBernatovic

But I still don't get proper class names, they are still randomized/minified. Is there anything else I should do?

Just connecting some dots, this was almost certainly because of https://github.com/styled-components/styled-components/issues/3635

velsa commented 2 years ago

Btw, for anyone interested, I eventually did the following:

  plugins: [
    // For all styled components:
    // create classnames from fileName and displayName in development
    react({
      babel: {
        presets: ['@babel/preset-typescript'],
        plugins: [
          '@babel/plugin-transform-typescript',
          [
            'babel-plugin-styled-components',
            {
              ssr: false,
              pure: true,
              displayName: true,
              fileName: false,
            },
          ],
        ],
      },
    }),
  ];

I added vite react plugin which accepts babel configuration and I am also doing TS transform ('@babel/plugin-transform-typescript) before using babel-plugin-styled-components.

This approach seems to play nicely with vite )

pawelczerepak commented 2 years ago

displayName worked fine with the babel macros described above but I had some issues with the css prop. In the end what worked for me is

import { defineConfig } from 'vite';
import reactPlugin from '@vitejs/plugin-react';
import macrosPlugin from "vite-plugin-babel-macros"

export default defineConfig({
  plugins: [
    macrosPlugin(),
    reactPlugin(),
  ],
});

and then import using import styled from "styled-components/macro";

Note that macros plugin is added before react plugin. babel-plugin-styled-components relies on JSX nodes in AST for the css prop to work. So this transform needs to happen before JSX gets transformed away. You also need to have the macro import in the same module where your css prop is used. Below the styled import is unused but without it the prop wouldn't work.

import React from "react";
import styled from "styled-components/macro";
import { someStyle } from "./styled";

export default function Component() {
    return <div css={someStyle} />;
}

Hope this helps someone.

oribenez commented 2 years ago

@rockwotj @ronaldruzicka Thank you for your responses! I tried to add config as per styled-components docs but it still didn't work. I tried many things but in the end, solution was even simpler than this approach. This is what worked for me in vite.config.js:

// vite.config.js

export default defineConfig({
  plugins: [
    react({
      babel: {
        plugins: [
          [
            'babel-plugin-styled-components',
            {
              displayName: true,
              fileName: false
            }
          ]
        ]
      }
    })
  ]
})

No need to install any other plugin for babel macros (nor vite nor babel macros plugin) and no need to change exports to import styled from 'styled-components/macro'.

not working on production. Did someone managed to make it work on production mode?

jurosh commented 2 years ago

@rockwotj @ronaldruzicka Thank you for your responses! I tried to add config as per styled-components docs but it still didn't work. I tried many things but in the end, solution was even simpler than this approach. This is what worked for me in vite.config.js:

// vite.config.js

export default defineConfig({
  plugins: [
    react({
      babel: {
        plugins: [
          [
            'babel-plugin-styled-components',
            {
              displayName: true,
              fileName: false
            }
          ]
        ]
      }
    })
  ]
})

No need to install any other plugin for babel macros (nor vite nor babel macros plugin) and no need to change exports to import styled from 'styled-components/macro'.

This works for me too, but needed to also add ssr: false. :warning: Without this hot-reload of styles was not reliable at all - changing properties works, but removing would just keep last value always persisted (until next reload).

react({
  babel: {
    plugins: [
      ['babel-plugin-styled-components', { ssr: false, pure: true, displayName: true, fileName: true }]
]}})

On the other hand I am not confident if this is proper solution or some kind of ugly thing we all hate from CRA (Rewire/Craco). Would be nice if Vite would be able to support such basic styled-components functionality out of the box or at least have documented how to do it correctly.

JulSeb42 commented 1 year ago

Hey, I'm trying to use vite-plugin-babel-macros inside my project. It works, but I'd like not to have the fileName displayed inside the generated class. I tried like this, but I still have the fileName inside the class:

export default defineConfig({
    plugins: [
        react({
            babel: {
                plugins: [
                    [
                        "babel-plugin-styled-components",
                        {
                            fileName: false,
                            displayName: true,
                            meaninglessFileNames: ["index", "styles"],
                        },
                    ],
                ],
            },
        }),
        macrosPlugin(),
    ],
})

How can I fix it? Thanks!

mlewando-cp commented 1 year ago

Hi guys I managed to have this config working in at least couple of ways (only displayName, namespace, fileName...) but the result is that elements has now classes with prefixes done accordingly to the configuration plus additional not prefixed class that is actually used in generate CSS. This is not what I expected, I hoped to have CSS styles assigned to prefixed classes. Is there a way to add some prefixes to classes that are actually used?

image

In the example above I need to have styles assigned to StyledDiv-gzSrWC and not hnYDyV as they are now (ideally class hnYDyV should not exist in the element at all)

This is the code that generated this example:

const StyledDiv = styled.div`
  color: red;
  padding: 10px;
  background-color: blue;
`;

const App = () => <StyledDiv>hello react</StyledDiv>;

and this is the config in vite.config.js:

    plugins: [
      react({
        babel: {
          plugins: [
            [
              "babel-plugin-styled-components",
              {
                ssr: false,
                pure: true,
                displayName: true,
                fileName: false,
              },
            ],
          ],
        },
      }),
basic-steve commented 1 year ago

@MeLlamoPablo works for me, thanks. For anyone who needs to customize the config a bit more, here all the available params: https://styled-components.com/docs/tooling

pradeepkumar24rk commented 1 year ago

@rockwotj @ronaldruzicka Thank you for your responses! I tried to add config as per styled-components docs but it still didn't work. I tried many things but in the end, solution was even simpler than this approach. This is what worked for me in vite.config.js:

// vite.config.js

export default defineConfig({
  plugins: [
    react({
      babel: {
        plugins: [
          [
            'babel-plugin-styled-components',
            {
              displayName: true,
              fileName: false
            }
          ]
        ]
      }
    })
  ]
})

No need to install any other plugin for babel macros (nor vite nor babel macros plugin) and no need to change exports to import styled from 'styled-components/macro'.

I also got the same issue my vite-config doesn't change anything it remains the same when I install vite.

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

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [react()],
})

I try a lot of things to solve this issue but till now I can't able to solve this issue in my system.

 "dependencies": {

    "eslint-plugin-styled": "^0.1.0",
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "styled-components": "^6.0.3",
    "vite-plugin-babel-macros": "^1.0.6"
  },
  "devDependencies": {
    "@types/react": "^18.2.14",
    "@types/react-dom": "^18.2.6",
    "@vitejs/plugin-react": "^4.0.1",
    "babel-plugin-styled-components": "^2.1.4",
    "eslint": "^8.44.0",
    "eslint-plugin-react": "^7.32.2",
    "eslint-plugin-react-hooks": "^4.6.0",
    "eslint-plugin-react-refresh": "^0.4.1",
    "vite": "^4.4.0"
  }

I also installed the babel macros but the issue is not solved

code app.js :

import styled from 'styled-components/macros';
function App() {

  return (
   <Container>
      hello
   </Container>
  )
}

export default App

const Container=styled.button`
  color: red;
`

issue :

15 | window.$RefreshSig$ = RefreshRuntime.createSignatureFunctionForTransform; 16 | } 17 | import styled from "styled-components/macros"; | ^ 18 | function App() { 19 | return / @PURE / jsxDEV(Container, { children: "hello" }, void 0, false, {

zibra commented 1 year ago

Disabling ssr helped in my case (Nx, Vite, latest versions of SC and babel plugin)

vostersc commented 1 year ago

Just leaving this for anyone stuck like I was. I needed an older version of styled-components in order to get the /macro folder working with my Vite/React/Styled-components project. The version I settled on was 5.3.0. I believe the folder was removed in v6.1 because of low usage. After getting the correct version, a compilation of the advice above worked for me.

bambery commented 10 months ago

So In 2024, what is the way to get this working? I have spent about two hours messing around with this and I still simply get an error every time I try to spin up my app. Debugging is terrible. Why is this a separate package and not a config for the base library? Who doesn't need readable class names when debugging?

Sheraff commented 10 months ago

So In 2024, what is the way to get this working? I have spent about two hours messing around with this and I still simply get an error every time I try to spin up my app. Debugging is terrible. Why is this a separate package and not a config for the base library? Who doesn't need readable class names when debugging?

I'm using the @vitejs/plugin-react-swc plugin, which itself accepts an array of plugins. This is the config I'm using

[
      '@swc/plugin-styled-components',
      {
        displayName: true,
        fileName: true,
        ssr: false,
      },
    ]

So basically no babel plugin, but if you can make the switch, this works perfectly fine.

aesy commented 10 months ago

@Sheraff Thanks for the pointer. For me, unfortunately, it gives me a deadlock when trying to build, no idea why. Would love to hear if it happens to anyone else and if they could resolve it.

wojtekmaj commented 10 months ago

@aesy You're not alone, @vitejs/plugin-react-swc doesn't seem to do the trick for me. During development, index.html loads, but after that, Vite just crashes silently.

hkrobotics commented 10 months ago

I am getting this error [vite] Pre-transform error: failed to handle: assertion failed: !result.is_null() (x2)

This is the config i am using

import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react-swc'
import { nodePolyfills } from 'vite-plugin-node-polyfills'

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [react({
    plugins: [[
      "@swc/plugin-styled-components",
      {
        displayName: true,
        ssr: true
      }
    ]]
  }), nodePolyfills()]
})

This error only comes up when i add @swc/plugin-styled-components plugin

unyo commented 10 months ago

For those having trouble with @swc/plugin-styled-components, I found luck with downgrading to @swc/plugin-styled-components@1.5.111: https://github.com/vitejs/vite-plugin-react-swc/issues/190#issuecomment-1907977402. With @swc/plugin-styled-components@1.5.115, vite was just crashing upon visiting http://localhost:5173/ in the browser, with this error:

zsh: bus error  

(Edit: I think maybe I was getting an error because I forgot to install @swc/core perhaps? latest @swc/core isn't working with @swc/plugin-styled-components@1.5.111, it errors out on build with:)

[vite:react-swc] failed to handle: failed to invoke plugin: failed to invoke plugin on 'Some...

Background

I've been trying to find a good replacement for create-react-app, all the new ones (next.js, remix, expo) can't be hosted at a /unknown-at-build-time/subpath/ - BrowserRouter doesn't work with relative paths (<base href="." />), and there isn't a good way to swap out BrowserRouter with a HashRouter. And remix build apparently just builds server JS files instead of static files like next.js and gatsby and expo do. It has an unstable SPA mode that uses vite - so I'm checking it out since I can't use any browser history routers. I've used gatsby before, but I don't feel like graphql today, so I'm not sure if it would work for this case yet.

So since I have to use HashRouter, this limits me to vite, parcel, or create-react-app. And it seems parcel does not work with @swc/plugin-styled-components at the moment (it doesn't seem to parse .swrc), so vite is my best hope for a CRA replacement at the moment (as create-react-app is being depreciated)

Parcel works too, but that would require babel, which would probably have the same relative performance as CRA. I'm concerned about Vite hitting the 10 download concurrent limit for lots of files in a large project, but I'm willing to check out SWC's performance for a bit since I already know the capabilities of babel and parcel (though not babel + parcel yet)

I'm trying to retain usage of styled-components since it has the vendor prefixing that I'd like to have automatically - some of these other built-in CSS solutions for CRA alternatives don't seem to have that, and I have to support older hardware. I've used PostCSS in the past, but styled-component vendor prefixing automatically and the automatically generated displayName (vs emotion's manual labeling) make styled-components kind of unreplaceable at the moment.

(Note: For SSR frameworks, only next.js with page routing seems to do SSR styled-components well. Next.js w/ app routing requires 'use client'. Remix generate SSR CSS too, but requires some Error Boundary files (ie _boundary._index.js) that I would rather not have in the codebase for someone to break.)

Solutions

The solutions below all show displayName: true behavior.

SWC Config 👍

install

npm install --save styled-components
npm install --save-dev @vitejs/plugin-react-swc @swc/plugin-styled-components @swc/core @vitejs/plugin-legacy terser

vite.config.js:

import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react-swc'
import legacy from '@vitejs/plugin-legacy'

export default defineConfig(({ mode }) => ({
  plugins: [
    react({ plugins: [["@swc/plugin-styled-components", {}]] }),
    legacy(),
  ],
  define: {
    'process.env.NODE_ENV': `'${mode || 'production'}'`, // browser throws an error about process not defined on determineTheme.ts
    'SC_DISABLE_SPEEDY': "true", // needed to enable vendor prefixing using 'vite build'
    // using process.env.SC_DISABLE_SPEEDY doesnt work due to the way styled-components checks for process.env 
    // https://github.com/styled-components/styled-components/blob/main/packages/styled-components/src/constants.ts#L17
    // I'm guessing process.env ternary worked in webpack but doesn't work with vite
    // you also need to wrap the app with <StyleSheetManager enableVendorPrefixes={true}>
  }
}))

package.json

  "dependencies": {
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "styled-components": "^6.1.8"
  },
  "devDependencies": {
    "@swc/core": "^1.3.106",
    "@swc/plugin-styled-components": "^1.5.115",
    "@vitejs/plugin-legacy": "^5.2.0",
    "@vitejs/plugin-react-swc": "^3.5.0",
    "terser": "^5.27.0",
    "vite": "^5.0.8"
  },

ref ref

Babel Config 👍

install

npm install --save styled-components
npm install --save-dev @vitejs/plugin-react babel-plugin-styled-components

vite.config.js

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

export default defineConfig({
  plugins: [react({
    babel: {
      plugins: [['styled-components', { displayName: true }]]
    },
  })],
  define: {
    'SC_DISABLE_SPEEDY': "true", // needed to enable vendor prefixing using 'vite build'
  }
})

package.json

  "dependencies": {
    "babel-plugin-styled-components": "^2.1.4",
    "react": "^18.2.0",
    "react-dom": "^18.2.0"
  },
  "devDependencies": {
    "@vitejs/plugin-react": "^4.2.1",
    "vite": "^5.0.8"
  }

ref

Babel Macros Config (depreciated, macros removed in styled-components 6) 🛑

install

npm install --save styled-components@5.3.0
npm install --save-dev vite-plugin-babel-macros @vitejs/plugin-react-swc

vite.config.js

import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react-swc'
import macrosPlugin from 'vite-plugin-babel-macros'

export default defineConfig({
  plugins: [
    macrosPlugin(),
    react()
  ],
  define: {
    'SC_DISABLE_SPEEDY': "true", // needed to enable vendor prefixing using 'vite build'
  }
})

package.json

  "dependencies": {
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "styled-components": "^5.3.0",
    "vite-plugin-babel-macros": "^1.0.6"
  },
  "devDependencies": {
    "@vitejs/plugin-react-swc": "^3.5.0",
    "vite": "^5.0.8"
  }

usage

import styled from 'styled-components/macro'

const Container = styled('div')`
  border: 4px solid green;
`

ref ref

More options?

bambery commented 10 months ago

Thank you all for your responses. I am hesitant to bind myself to old versions, and as I am beginning a new project, I think I will simply opt for a different way to manage styling inside of React rather than using styled components.

unyo commented 10 months ago

@bambery after some investigation I was able to get it working with the latest @swc/plugin-styled-components

https://github.com/unyo/vite-swc-styled-components-legacy