Search code examples
reactjsthemesstyled-components

Dynamic Theme in Styled Components


I'm using styled-components in my React app and wanting to use a dynamic theme. Some areas it will use my dark theme, some will use the light. Because the styled components have to be declared outside of the component they are used in, how do we pass through a theme dynamically?


Solution

  • That's exactly what the ThemeProvider component is for!

    Your styled components have access to a special theme prop when they interpolate a function:

    const Button = styled.button`
      background: ${props => props.theme.primary};
    `
    

    This <Button /> component will now respond dynamically to a theme defined by a ThemeProvider. How do you define a theme? Pass any object to the theme prop of the ThemeProvider:

    const theme = {
      primary: 'palevioletred',
    };
    
    <ThemeProvider theme={theme}>
      <Button>I'm now palevioletred!</Button>
    </ThemeProvider>
    

    We provide the theme to your styled components via context, meaning no matter how many components or DOM nodes are in between the component and the ThemeProvider it'll still work exactly the same:

    const theme = {
      primary: 'palevioletred',
    };
    
    <ThemeProvider theme={theme}>
      <div>
        <SidebarContainer>
          <Sidebar>
            <Button>I'm still palevioletred!</Button>
          </Sidebar>
        </SidebarContainer>
      </div>
    </ThemeProvider>
    

    This means you can wrap your entire app in a single ThemeProvider, and all of your styled components will get that theme. You can swap that one property out dynamically to change between a light and a dark theme!

    You can have as few or as many ThemeProviders in your app as you want. Most apps will only need one to wrap the entire app, but to have a part of your app be light themed and some other part dark themed you would just wrap them in two ThemeProviders that have different themes:

    const darkTheme = {
      primary: 'black',
    };
    
    const lightTheme = {
      primary: 'white',
    };
    
    <div>
      <ThemeProvider theme={lightTheme}>
        <Main />
      </ThemeProvider>
    
      <ThemeProvider theme={darkTheme}>
        <Sidebar />
      </ThemeProvider>
    </div>
    

    Any styled component anywhere inside Main will now be light themed, and any styled component anywhere inside Sidebar will be dark themed. They adapt depending on which area of the application they are rendered in, and you don't have to do anything to make it happen! 🎉

    I encourage you to check out our docs about theming, as styled-components was very much built with that in mind.

    One of the big pain points of styles in JS before styled-components existed was that the previous libraries did encapsulation and colocation of styles very well, but none of them had proper theming support. If you want to learn more about other pain points we had with existing libraries I'd encourage you to watch my talk at ReactNL where I released styled-components. (note: styled-components' first appearance is at ~25 minutes in, don't be surprised!)