loading...
Cover image for How to use Themes in styled-components

How to use Themes in styled-components

aromanarguello profile image aromanarguello Updated on ・4 min read

Styled-components is my default css-in-js library. I have used other libraries in the past but styled-components remains my personal favorite. I love the fact that I can embed and treat styles as if they were components. It gives me the ability to use maximum reusability. Additionally, I can pass props to these and make really cool conditional styles with minimum effort.

Theming is a great tool to use within front-end applications. It also allows me to write way less code and be more consistent with my styling. By leveraging the theme provider in styled-components I only need to write my main styles and rules in one single object and invoke them in any component that is a descendant of that provider. Theme provider achieves this by leveraging the context API.

This short guide assumes a basic knowledge of styled-components.

I created a code sandbox with only three files in it, but the Theme pattern I use can be added anywhere within the directory of your application.

Update June 2020: I hosted a small workshop where I show how to add styled-components with ThemeProvider to a react app. You can checkout the video here. If you want to skip directly to theme provider, you can jump to minute 26:27.

Given the following structure:

|_ public
|_ src
  |_ App.jsx
  |_ index.js
  |_ Theme.jsx // can go in any folder
|_ package.json

App.jsx

import React from "react";

const App = () => {
  return (
      <div>
        <h1>Hello world!</h1>
        <h2>By the power of styled-components!</h2>
      </div>
  );
};
export default App;

To begin with, inside of your react project install styled-components

npm install styled-components

After I have set up my initial configuration and installed all my dependencies I usually go ahead and create a Theme.jsx component. In this component, I also create the theme object file. You can easily create this within your App.jsx component, but I think it is a good separation of concerns to allow it to live in its own file.

First things first, import React from the react dependency, and then we import ThemeProvider from the styled-components file. Theme provider uses context to provide the properties to its descendants.

import React from "react";
import { ThemeProvider } from "styled-components";

Theme provider takes in a prop called theme, this props takes an object. We can add any property we want our styled-components to have access to. You can let your imagination run wild here! It is very powerful.

const theme = {
  colors: {
    powderWhite: "#FFFDF9",
    persianGreen: "#06B49A",
    lightBlue: "#AFDBD2",
    onyx: "#36313D"
  },
  fonts: ["sans-serif", "Roboto"],
  fontSizes: {
    small: "1em",
    medium: "2em",
    large: "3em"
  }
}

Then, using the render props method we will create a Theme component that will render children with all the properties from the Theme provider. Like I mentioned before, the theme provider takes a theme prop. In that prop is where we reference the object we create above.

const Theme = ({ children }) => (
  <ThemeProvider theme={theme}>{children}</ThemeProvider>
);

Finally, we export the theme.

import React from "react";
import { ThemeProvider } from "styled-components";

const theme = {
  colors: {
    powderWhite: "#FFFDF9",
    persianGreen: "#06B49A",
    lightBlue: "#AFDBD2",
    onyx: "#36313D"
  },
  fonts: ["sans-serif", "Roboto"],
  fontSizes: {
    small: "1em",
    medium: "2em",
    large: "3em"
  }
};

const Theme = ({ children }) => (
  <ThemeProvider theme={theme}>{children}</ThemeProvider>
);

export default Theme;

Back in our App.jsx the component, we now import the theme component that was just created to wrap our entire function. By wrapping our App function, all the descendants of App will have access to the theme object. I usually like to add it to the top level of my project.

We will also import the styled object. In a nutshell, the way we create styled-components is through this method. This method will give us back an HTML element that is mapped to the components and applies the give CSS styles.

import React from "react";
import Theme from "./Theme";
import styled from "styled-components"
const App = () => {
  return (
    <Theme>
      <div>
        <h1>Hello world!</h1>
        <h2>By the power of styled-components!</h2>
      </div>
    </Theme>
  );
};
export default App;

Now, let's create some quick styled-components to highlight how we can access the theme within our styles. I also added props to <Heading> to illustrate how we can pass prop to our styles and use them to do things like conditionally render a font size, but you can do so much more. As I said, you can use your imagination 😁. You can perform any javascript operation. And because styled-components uses template literals, it all feels as if we were writing normal CSS.

import React from "react";
import Theme from "./Theme";
import styled from "styled-components";

const Container = styled.div`
  width: 100%;
  border: ${props => `1px solid ${props.theme.colors.onyx}`};
  background-color: ${props => props.theme.colors.lightBlue};
  font-family: ${props => props.theme.fonts[0]};
`;

const Heading = styled.h1`
  font-size: ${({ isHeading, theme: { fontSizes } }) =>
    isHeading ? fontSizes.large : fontSizes.small};
  color: ${({ theme: { colors } }) => colors.persianGreen};
`;

const App = () => {
  return (
    <Theme>
      <Container>
        <Heading isHeading={true}>Hello World</Heading>
        <h2>By the power of styled-components!</h2>
      </Container>
    </Theme>
  );
};
export default App;

In the <Container> styles I opted out of destructuring in order to show that the concept of theming is possible via the props system. All we are doing is telling our props that we want to access the theme object that lives there. Inside the theme object, we can access any of the keys such as the colors object and reference a specific color (i.e persianGreen).

I personally prefer to destructure my objects such as in <Heading>, in my opinion, it looks cleaner and I prefer less repetition of long chaining.

The props object as a whole now contains two main properties, theme and isHeading, but we can add as many props as we like. A way to great leverage this, is to pass a value from state and tie the style to rendered on state change.

You can start using themes in styled-components really quick as you can see. If you need clarification on any concept from this post, don't hesitate to reach out via comments or spectrum.chat :)!

Thanks for reading!

Posted on by:

Discussion

pic
Editor guide
 

How would you handle responsive designs in theming?
Basically, fontSize for different layouts(mobile)?

 

Hey! Great question!

I usually do this by creating an object inside the theme object.

something like:

const theme = {
//..other theme objects,
fontSizes: {
  sm: 12,
  m: 14,
  l: 16,
  xl: 22
}

Or you could put them on an array and then map them. Does this answer your question? If not would be glad to provide more examples :D

You could also name them differently based on the layout. Or create an object of layouts, with all the properties for the size inside one object.

const theme = {
//..other theme objects,
mobile: {
  fontSize: 12,
  margin: '4px',
  color: 'red',
}
 

Hey,
Thank you for your reply. I really loved your article and I am following the same approach.

However, my question is around the snippet i have attached.
Basically, in my theme i am setting common styles for my input fields which will be reused in my website.
How do i manage the commented bit?

code snippet

Are you trying to include in the commented part in the theme object?

@media ...// {
   fontSize: 12px
}

If so, I am not sure if this is possible, at least I can't think of a way to do it 😅 The way I would manage it would be something like this. I would create a reusable input component that I could import into my other components. Something that would look like this 👇

const Input = styled.input`
  // all your other inputFields styles
  @media (min-width: ${({
      theme: {
        breakPoints: { mobileS }
      }
    }) => mobileS}) {
    fontSize: ${({ theme: { fontSizes } }) => `${fontSizes[1]}px` }
  }
`

And in my theme object:

const theme = {
  fontSizes: [12, 14, 16, 18, 20],
  breakPoints: {
    mobileS: '320px',
    mobileM: '375px',
    mobileL: '425px'
  }
}

If you were to kebab-case your keys in the theme object, you could theoretically also be able to ...(spread) them on to your component something like: ${props => ...props}

I assumed the same that there isn't a straightforward way to handle the various mobile sizes.
However, thanks for replying :)

If you are still looking for something you should try github.com/morajabi/styled-media-q...

 

Excellent written and straight to the point, thanks!