Styled Components 101 ๐ Lecture 2: Creating a theme + Light/Dark theme toggler example โ๏ธ๐
Hello, everybody! ๐ And welcome to the 2nd lecture of the Styled Components 101 series ๐
In this lecture, we'll be covering the creation of a custom theme with Styled Components.
If you're new to Styled Components and this lecture is the first one you run into, I suggest taking a look at the previous lecture first, where we covered some basic concepts and examples of Styled Components.
With all this said, let's move on to today's topic ๐
Creating the theme file ๐ฎ
First things first. Before start generating components and other component-related stuff, what we need to do is create the file that will host our app's theme.
This theme file will be just a regular JavaScript file with js extension that will contain an object called theme with properties and values that we'll be using to style our components.
As easy as that ๐โโ๏ธ
Let's have a look at how this file looks like:
theme.js
const theme = {
colors: {
plum: "#52314b",
rose: "#c3537d",
dryrose: "#dd7f9a",
primrose: "#e8babf",
white: "#fff"
}
}
export default theme;
For now, we'll be working only with colors, but every property/value you can imagine that is used for styling will also be set in this file: values regarding fonts, padding, margin, etc. In short, every value that you'd set up in a CSS file.
Don't forget to export your theme object. Otherwise, there's no way for it to be found by the app ๐
And that's all for the theme file. We're now set to head on to the next step ๐ผ
Making the theme file reachable by the app ๐ต๏ธ
In order for the theme file to be able to be accessed, we need to provide it to the app. To do this, we'll import the component ThemeProvider from styled-components and will wrap our app with it.
App.js
import { ThemeProvider } from "styled-components";
import theme from "./theme";
const App = () => {
return(
<ThemeProvider theme={theme}>
//your code here
</ThemeProvider>
)
}
export default App;
Now, our theme is recognized by the app and can be accessed and used from anywhere throughout it.
Using the theme inside a styled component ๐
Retrieving theme values from within a styled component is a really easy process, since the theme is implicit to the component props.
Let's see how to style a styled component making use of a couple values from our custom theme:
BasicThemedButton.js
import styled from "styled-components";
export default styled.button`
background-color: ${props => props.theme.colors.primrose};
color: ${props => props.theme.colors.white};
font-size: 1.25rem;
border: none;
border-radius: 5px;
padding: 10px;
`
Our custom theme is implicitly passed-in as a prop and it's easily accessed because the component is also wrapped by the provider:
App.js
import { ThemeProvider } from "styled-components";
import theme from "./theme";
import BasicThemedButton from "./components/BasicThemedButton";
const App = () => {
return(
<ThemeProvider theme={theme}>
<BasicThemedButton>I am a basic themed button! ๐ค</BasicThemedButton>
</ThemeProvider>
)
}
export default App;
Using the theme inside a custom React component โ๏ธ
To use our theme inside a custom React component, we need to write a little bit more code, but not a big deal at all.
ThemedButton.js
โก React component
import React, { useContext } from "react";
import StyledButton from "./StyledButton";
import { ThemeContext } from "styled-components";
const ThemedButton = () => {
const theme = useContext(ThemeContext);
return(
<StyledButton bgColor={theme.colors.plum} color={theme.colors.white}>I am a themed button ๐</StyledButton>
)
}
export default ThemedButton;
StyledButton.js
โก Styled component
import styled from "styled-components";
export default styled.button`
background-color: ${props => props.bgColor};
color: ${props => props.color};
padding: 10px;
border: none;
border-radius: 5px;
font-size: 1.25rem;
`
This time, the values from the theme are passed to the styled component via props, which have been previously retrieved in the React component by making use of the useContext hook and the ThemeContext component from styled-components.
Note that we'll be calling our React component as we regularly do:
App.js
import { ThemeProvider } from "styled-components";
import theme from "./theme";
import ThemedButton from "./components/ThemedButton";
const App = () => {
return(
<ThemeProvider theme={theme}>
<ThemedButton />
</ThemeProvider>
)
}
export default App;
The theme prop ๐ค
The theme prop is used to pass in an inline object that contains the specification for the theme to a styled component.
App.js
import { ThemeProvider } from "styled-components";
import StyledButton from "./components/StyledButton";
import theme from "./theme";
const App = () => {
return(
<ThemeProvider theme={theme}>
<StyledButton theme={{bg: theme.colors.primrose, color: theme.colors.white}}>I am a button with a theme prop! ๐</StyledButton>
</ThemeProvider>
)
}
export default App;
StyledButton.js
import styled from "styled-components";
export default styled.button`
background-color: ${props => props.theme.bg};
color: ${props => props.theme.color};
padding: 10px;
border: none;
border-radius: 5px;
`
This method can be useful if we don't have many properties, but, when we have a robust application with several properties and values for styling, this method becomes difficult to maintain and tricky to deal with โ
Using JavaScript objects to write our styles rather than CSS ๐คนโโ๏ธ
Did you know that is also possible to write custom styles using JS instead of CSS in Styled Components? And it's easier than you may think.
Let's have a look at the following snippet:
App.js
import { ThemeProvider } from "styled-components";
import JSStyledButton from "./components/JSStyledButton";
import theme from "./theme";
const App = () => {
return(
<ThemeProvider theme={theme}>
<JSStyledButton bgColor={theme.colors.plum} color={theme.colors.white}>I am a JS Styled Button ๐ค </JSStyledButton>
</ThemeProvider>
)
}
export default App;
JSStyledButton.js
import styled from "styled-components";
export default styled.button(props => ({
backgroundColor: props.bgColor,
fontSize: "1.5rem",
color: props.color
}));
Notice that property names are written in camelcase notation, as we're using a JavaScript object, but it's also accepted to write them within double quotes, like this:
import styled from "styled-components";
export default styled.button(props => ({
"background-color": props.bgColor,
"font-size": "1.5rem",
color: props.color
}));
Also note that the used method to specify the styles is similar to the one we use when extending styles from a supercomponent (Lecture 1 - Inheriting styles from another component section):
...
export default styled.button(...);
...
And now... ๐ The jewel in the crown: Let's create a Light/Dark theme toggler with Styled Components
Theory is cool but, but let's get our hands dirty and let's create something interesting using themes ๐
Excuse me, but... what exactly is a theme toggler? ๐ค
We can say that a theme toggler is a system that allows to switch between a light and a dark theme with just one click by using a button or an emoji. Cool, right?
In this tutorial, we'll implement a styled button that will perform this task really fast and efficiently.
And now, let's get it started ๐
The components and files ๐ฌ
These are the components and files that will make up our app:
Button.js
: the button that will switch between themes.
GlobalStyles.js
: a component that will inject global styles into the app.
ThemeToggler.js
: the component that will receive the theme as a prop, which, in turn, will be passed in to the Button component.
useDarkTheme.js
: a custom hook that will contain the business logic.
App.js
: the main app. It will wrap everything up with the theme provider and will include the global styles.
themes.js
: the file that will host our themes (we already know how to do this at this point ๐บ).
Let's first create our themes file ๐
themes.js
export const lightTheme = {
background: '#fff',
color: '#1d1f28',
buttonBg: '#c5718d'
}
export const darkTheme = {
background: '#1d1f28',
color: '#fafafa',
buttonBg: '#515d90'
}
Not a lot to remark here: we have defined both themes with their respective style properties. Don't forget to export them both ๐
Now let's go with the switch button ๐ต
Button.js
import styled from "styled-components";
export default styled.button`
font-family: "Monaco", monospace;
cursor: pointer;
border: none;
background-color: ${({ theme }) => theme.buttonBg};
color: #fff;
padding: 10px;
border-radius: 5px;
font-size: 1.5rem;
`
A regular styled button. You can style it as you like best.
Note that the value for background-color
will be set depending on the selected theme. The rest is up to you ๐
Time for global styles ๐ผ
GlobalStyles.js
import { createGlobalStyle } from "styled-components";
export const GlobalStyles = createGlobalStyle`
body {
font-family: "Monaco", monospace;
background: ${({ theme }) => theme.background};
color: ${({ theme }) => theme.color};
transition: all 0.50s linear;
}
`
GlobalStyles defines the global styles for our app. They will be injected into our app by in App.js
.
Note that for this purpose, we'll import the createGlobalStyle helper from styled-components, which will create a new styled component that will handle global styles.
Regarding the properties, background and text colors will be retrieved from the theme. We'll also add a transition property to make the switch effect smoother.
Creating the theme toggler ๐
ThemeToggler.js
import React from 'react';
import Button from "./Button";
const ThemeToggler = (props) => {
const { themeToggler } = props;
return (
<Button onClick={themeToggler}>Switch Theme โ๏ธ ๐</Button>
);
};
export default ThemeToggler;
ThemeToggler renders the Button component and provides it with the style properties that correspond to the passed-in theme.
useDarkTheme.js
import { useState } from 'react';
const THEMES = {
LIGHT: "light",
DARK: "dark"
}
const useDarkTheme = () => {
const [theme, setTheme] = useState(THEMES.LIGHT);
const themeToggler = () => {
theme === THEMES.LIGHT ? setTheme(THEMES.DARK) : setTheme(THEMES.LIGHT);
};
return [theme, themeToggler];
};
export default useDarkTheme;
useDarkTheme is a custom hook that contains the logic for our toggler. We're using a hook to abstract our app as much as possible.
The switching procedure is as easy as follows: if the passed-in theme is light, the dark theme will be set up, and vice versa.
Building the main app ๐
App.js
import React from "react";
import { ThemeProvider } from "styled-components";
import { GlobalStyles } from "./components/GlobalStyles";
import { lightTheme, darkTheme } from "./themes";
import ThemeToggler from "./components/ThemeToggler";
import useDarkTheme from "./hooks/useDarkTheme";
const App = () => {
const [theme, themeToggler] =
useDarkTheme();
const selectedTheme = theme === 'light' ? lightTheme : darkTheme;
return (
<ThemeProvider theme={selectedTheme}>
<>
<GlobalStyles />
<ThemeToggler themeToggler={themeToggler} />
<p>Trying out light and dark themes! ๐</p>
</>
</ThemeProvider>
)
}
export default App;
First, we're calling the useDarkTheme hook, that will handle the switch logic.
Then, we also need to have a function selectedTheme, that decides which theme is going to be used.
And now, what's only left is to wrap up ThemeToggler with ThemeProvider and include GlobalStyles.
The result โ๏ธ โก๏ธ ๐
And this is all for the second Styled Components 101 lecture!
Stay tuned to learn more about Styled Component in future episodes of the series.
A big thanks for reading ๐ค and don't hesitate to reach out to me if you any questions or doubts about today's lecture.
I hope you found this article useful and I see you all in the next ๐
๐ You can also check out the related slides for this article on Instagram ๐
๐ Don't forget to follow @underscorecode on Instagram and Twitter for more daily webdev content ๐ฅ๐ค