Theming styled-components

Flavio Copes

💅 styled-components are a great way to handle CSS in React. If you’re new to styled-components check out our article styled-components, the Modern Way to Handle CSS in React for an introduction.

In this post we’re going to create a simple email newsletter subscription form and use styled-components to handle the visual appearance.

Setup using React Boilerplate

We’ll start with setting up a React Boilerplate application. If you prefer however, you can also use Create React App to get started.

Check out https://www.reactboilerplate.com/ for a detailed explanation of this boilerplate.

To create a new project we just clone the boilerplate GitHub repository:

$ git clone --depth=1 https://github.com/react-boilerplate/react-boilerplate.git

And we remove the example app to start from a clean slate and then start the app:

$ npm run clean
$ npm start

Writing a Sample Form

Now let’s build the email signup form. We create a signup form in its own component, using styled-components to render it nicely.

Let’s create four styled-components: Form, Title, Button and Input.

Each component represents a small portion of our form, and we add a few CSS rules for each one:

components/SignupForm/index.js

import React from 'react';
import styled from 'styled-components';

const Form = styled.form`
  margin: 0 auto;
  width: 50%;
  min-width: 400px;
  max-width: 800px;
  text-align: center;
  border: 1px solid #ddd;
  padding-top: 0px;
  padding-bottom: 90px;
  color: black;
  background: white;
`;

const Title = styled.h2`
  margin-top: 40px;
  margin-bottom: 70px;
  font-size: 1.5em;
  color: black;
  background-color: white;
`;

const Button = styled.button`
  font-size: 1.5em;
  background-color: black;
  color: white;
`;

const Input = styled.input`
  font-size: 1.45em;
  border: 1px solid #ddd;
`;

Then we create a simple component that handles the submit event with a simple console.log() that prints out the given email, as we just want to focus on the styling part:

import React from 'react';

export default class SignupForm extends React.Component {

  signUp = (e) => {
    const email = new FormData(e.target).get('email');
    e.preventDefault();
    console.log(`New signup from ${email}`);
  }

  render() {
    return (
      <Form onSubmit={this.signUp}>
        <Title>
          Sign up to my newsletter
        </Title>
        <Input type="email" name="email" />
        <Button>Sign up</Button>
      </Form>
    );
  }
}

Next, we just import it in the HomePage component that React Boilerplate created for us, and we render <SignupForm />:

containers/HomePage/index.js

import React from 'react';
import SignupForm from '../../components/SignupForm';

export default class HomePage extends React.Component {
  render() {
    return (
      <SignupForm />
    );
  }
}

That’s it, the form should now display nicely in our application:

Single component using styled-components

In the CSS we have a few different properties that are used by different components: the background and foreground colors, the border color and the text color.

They now have fixed colors because we had the form on just one page, but the requirements changed and now we are told the form must be added to a variety of pages with different backgrounds and color schemes.

We need to make the component reusable, and we can do so using a theme.

Creating a Theme

A theme is created by wrapping all our components in a ThemeProvider wrapper component, and by referencing the properties of props.theme in our styled-components CSS:

import React from 'react';
import styled, { ThemeProvider } from 'styled-components';

const Form = styled.form`
  /* other properties */
  border: 1px solid ${(props) => props.theme.borderColor};
  color: ${(props) => props.theme.primaryColor};
  background-color: ${(props) => props.theme.secondaryColor};
`;

const Title = styled.h2`
  /* other properties */
  background-color: ${(props) => props.theme.secondaryColor};
  color: ${(props) => props.theme.primaryColor};
`;

const Button = styled.button`
  /* other properties */
  background-color: ${(props) => props.theme.primaryColor};
  color: ${(props) => props.theme.secondaryColor};
`;

const Input = styled.input`
  /* other properties */
  border: 1px solid ${(props) => props.theme.borderColor};
`;

We create a theme object, which is an object literal with the properties we reference in our styled-components:

const theme = {
  secondaryColor: 'white',
  primaryColor: 'black',
  borderColor: '#ccc',
};

and finally we wrap the Form component in a ThemeProvider wrapper component:

export default class SignupForm extends React.Component {

  /* ... */

  render() {
    return (
      <ThemeProvider theme={theme}>
        <Form onSubmit={this.signUp}>
          <Title>
            Sign up to my newsletter
          </Title>
          <Input type="email" name="email" />
          <Button>Sign up</Button>
        </Form>
      </ThemeProvider>
    );
  }
}

This is the basic method for theming using styled-components. We put all the theme styles in the theme object and all components use those styles.

Creating Reusable Themes

We will now add the signup form twice to a page. The first time the background of the page is bright, and the second time it’s dark.

We do so with some simple CSS rules added to app/global-styles.css, a file included automatically by React Boilerplate:

.container.dark {
  background: #282828;
}

We can wrap the SignupForm components in a class that uses the container class, and the presence of a dark class will make it dark.

containers/HomePage/index.js

import React from 'react';
import SignupForm from '../../components/SignupForm';

export default class HomePage extends React.Component {
  render() {
    return (
      <main>
        <div className="container">
          <SignupForm />
        </div>
        <div className="container dark">
          <SignupForm />
        </div>
      </main>
    );
  }
}

Two components render with the same style on different backgrounds

What if we want the second form to blend nicely in the dark background, and not stand out like a sore thumb? We add a dark prop on SignupForm in the HomePage component’s render() method:

return (
  <main>
    <div className="container bright">
      <SignupForm />
    </div>
    <div className="container dark">
      <SignupForm dark />
    </div>
  </main>
);

And we look for the prop it in the SignupForm component’s constructor() method, and, if present, we use a dark theme:

export default class SignupForm extends React.Component {

  constructor(props) {
    super(props);

    this.theme = {
      secondaryColor: 'white',
      primaryColor: '#282828',
      borderColor: '#ccc',
    };

    if (props.dark) {
      this.theme.secondaryColor = '#282828';
      this.theme.primaryColor = '#fff';
    }
  }

  /* ... */

  render() {
    return (
      <ThemeProvider theme={this.theme}>
        <Form onSubmit={this.signUp}>
          <Title>
            Sign up to my newsletter
          </Title>
          <Input type="email" name="email" />
          <Button>Sign up</Button>
        </Form>
      </ThemeProvider>
    );
  }
}

We now have the result we wanted, fitting more nicely into the dark background:

The final result of our theming, the component with multiple themes


This method allows us to play with different presentations by creating a convention of properties, and by editing the theme based on the style we need.

  Tweet It

🕵 Search Results

🔎 Searching...