9

The following question came up when I wanted to migrate from Styled Components to CSS Modules.

Let's say I have the following styled component which accepts a dynamic parameter offset and a dynamic CSS string theme:

const Li = styled.li`
  &.selected {
    background-color: grey;
  }

  margin-left: ${({ offset }) => offset}px;

  ${({ theme }) => theme};
`;

In my code, I'd use it the following way:

const Parent = () => (
  <List>
    {list.map((item) => (
      <Item
        key={item.id}
        id={item.id}
        selectedIds={[]}
        offset={24}
        theme={`
          &.selected {
            background-color: green;
          }
        `}
      >
        {item.name}
      </Item>
    ))}
  </List>
);

const Item = ({ id, offset = 0, theme, children }) => {
  return (
    <Li
      theme={theme}
      offset={offset}
      className={selectedIds.includes(id) && 'selected'}
    >
      {children}
    </Li>
  );
};

Requirement: Now I would really keep the Item's component API: passing a number offset and a style string theme. So essentially everything in Parent component should stay this way.

How can I convert the Item component internally to use CSS Modules instead of the styled Li component?

2 Answers 2

11
+50

It's probably a different way of thinking than you used to but it can work

  1. You can use css variable
style={{ [`--offset`]: `${offset}px` }}
.item {
  margin-left: var(--offset);
}
  1. You can have a css module (file) dedicated to themes. In your case, it has withSelected
.withSelected {
  &.selected {
    background-color: green;
  }
}

So you could pass it as "theme"

theme={themes.withSelected}

This is how the components look

import styles from "./style.module.scss";
import themes from "./themes.module.scss";

const Parent = () => (
  <ul>
    {list.map((item) => (
      <Item
        key={item.id}
        id={item.id}
        selectedIds={[1]}
        offset={24}
        theme={themes.withSelected}
      >
        {item.name}
      </Item>
    ))}
  </ul>
);

const Item = ({ id, offset = 0, theme, children, selectedIds }) => {
  return (
    <li
      className={`${styles.item} ${theme} ${
        selectedIds.includes(id) && themes.selected
      }`}
      style={{ [`--offset`]: `${offset}px` }}
    >
      {children}
    </li>
  );
};

Demo: https://codesandbox.io/s/styledcomponent-to-css-modules-1kbqx

Sign up to request clarification or add additional context in comments.

5 Comments

Thanks for the answers Mosh! The 1. solution would be a good way of doing it! For the 2., I would expect something more ambiguous than having to use CSS Modules for passing it as theme. I would want to pass it as style string as I have done in the question. How can I pick this up in Item component then?
Edited the question to reflect more what I want to achieve here.
There is an advantage of the possibility to pass css as prop (I'm not sure how scalable it is though). But, one of the differences in styledComponents ("SC") is that js involved so you can manipulate the styles in runtime. So SC gives you (1) runtime manipulations (2) css scoping. css-modules like vanilla css is static and gives you only css-scoping. That's why a new actually, old) way of thinking required when you move from SC to css-modules.
Won't work with typescript
@MegaProger what do you mean by "won't work with typescript"? Would you mind to elaborate? I made a snippet with a ts version for the discussion
1

With 1 I'd concur with @Mosh to just use the style prop. Css modules are static by design and there no way to get this done otherwise (I think that styled-components also uses the style prop so you're not losing anything).

For 2 you can leverage Sass modules which allow you to define your theme in a single place and import them as required:

/theme/_colors.scss

$red: rgb(200, 0 50);

/components/Item.module.scss

@import "../theme/colors"

.selected {
    background: $red;
}

Note: if you use Create React App absolute paths you can import from root as ~theme/colors

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.