Search code examples
reactjsstyled-components

Is ThemeProvider necessary for theming in styled components?


I am using styled components, I am wondering if it is necessary to use a ThemeProvider.

This is the example in the documentation.

// Define our button, but with the use of props.theme this time
const Button = styled.button`
  font-size: 1em;
  margin: 1em;
  padding: 0.25em 1em;
  border-radius: 3px;

  /* Color the border and text with theme.main */
  color: ${props => props.theme.main};
  border: 2px solid ${props => props.theme.main};
`;

// We are passing a default theme for Buttons that arent wrapped in the ThemeProvider
Button.defaultProps = {
  theme: {
    main: "palevioletred"
  }
}

// Define what props.theme will look like
const theme = {
  main: "mediumseagreen"
};

render(
  <div>
    <Button>Normal</Button>

    <ThemeProvider theme={theme}>
      <Button>Themed</Button>
    </ThemeProvider>
  </div>
);

But I am wondering if I can just do this:

// theme.js
const theme = {
  main: "mediumseagreen"
};
export default theme;


// main.js
import theme from 'theme';

const Button = styled.button`
  font-size: 1em;
  margin: 1em;
  padding: 0.25em 1em;
  border-radius: 3px;

  /* Color the border and text with theme.main */
  color: ${theme.main};
  border: 2px solid ${theme.main};
`;

  render(
  <div>    
      <Button>Themed</Button>
  </div>
);

If I don't need to a Normal Button (all Buttons will be themed), and I don't need dynamic theming (e.g change to a dark mode), could I use the second approach? are there any drawbacks I didn't think of?


Solution

  • Yes, this is valid code.

    But it has some drawbacks. ThemeProvider provides a theme by using the React Context API. Via the Context API information is passed down a component tree so that every child has access to the context and can access the including information (theme in this case). By doing it the way you suggested, you get some limitations:

    1. Any changes in your theme will not be reflected in your components, because they don't re-render if external values change. They only re-render on property and state changes. But as you wrote, you don't need that.
    2. In every component, where you want to use your theme you have to explicitly import it, which is not necessary if you use ThemeProvider;
    3. If you only want to apply a theme to a single branch of your component tree, it is really hard to keep track, where you can import a given theme and where not. Especially if you reuse components in different branches of your tree. But you said you want to use only one theme for all.
    4. It just isn't idiomatic react code and you risk at least one more WTF? in your code which may reduce your code quality.