Changing App Themes Using React Native, Styled Components and Redux
October 2nd, 2019 | By Aman Mittal | 9 min read
Changing app themes is about styling our React Native apps using Styled Components and switching between them using Redux. If you are getting into React Native or have dipped your toes, you know there are different ways to style a React Native app.
React Native uses JavaScript objects by default. If you have some experience with the CSS of the web, you know that styling a component is nothing more than writing code using proper styling syntax.
Redux is a third-party open-source library. Using it is a matter of choice, but it is also a way to add styling to your app, and many might find it easy to use.
Requirements
Make sure you have the following installed on your local development environment and have access to the services mentioned below:
Nodejs (>=10.x.x) with npm and yarn installed
React-native-cli
Mac users must be running an iOS simulator.
Windows and Linux users must be running an Android emulator.
To learn more about how to set up a development environment for React Native using react-native-cli, please refer to the official documentation.
You can find the complete code for this tutorial in this GitHub repository.
Installing styled-components
You already have a new React Native project created using the command react-native init StyledThemeApp from a terminal window.
Now go to the inside of the newly generated directory. When inside it, execute the following command to install the styled-components library:
npm install styled-components
That's all you need to do to use it in your React Native app!
Styled Components is a CSS-in-JS library that enables developers to write each component with its style and allows the code to be in a single location. Coupling your styles with the components provides a way to optimize the developer experience and output.
Let us create a simple component that will act as the primary screen of the app.
Create a new file inside screens/HomeScreen.js. It is a class component that displays text inside a box. The visual components are created using styled components. To consume this library, write an import statement from styled-components/native.
import React from 'react'
import styled from 'styled-components/native'
const Container = styled.SafeAreaView`
flex: 1;
background-color: papayawhip;
justify-content: center;
align-items: center;
`
const TextContainer = styled.View`
padding: 15px;
border-radius: 5px;
border: 1px solid palevioletred;
`
const Title = styled.Text`
padding: 20px;
font-size: 24px;
font-weight: 500;
color: palevioletred;
`
class HomeScreen extends React.Component {
render() {
return (
<Container>
<TextContainer>
<Title>Themed App with React Native & Styled Components</Title>
</TextContainer>
</Container>
)
}
}
export default HomeScreen
Styled components utilize tagged template literals to style your components using backticks.
The Container and the TextContainer are React Native’s views and have styling attached to them. The Title uses Text from React Native. The styled-components library uses the same flexbox model as React Native Layouts. The advantage here is that you get to write styles in the same understandable syntax that you have been using in web development and standard CSS.
Import the HomeScreen component inside the entry point file, App.js. Replace its existing content with the following:
import React from 'react'
import HomeScreen from './screens/HomeScreen'
const App = () => {
return <HomeScreen />
}
export default App
Open the app in a simulator. You can execute the commands from the terminal window, depending on the mobile platform you are using.
# for ios
react-native run-ios
# for android
react-native run-android
You will get the following result:
Define Themes
In the current React Native app, you will use the classic example of a dark and a light mode.
Create a new file called /styles/theme.js. It will contain the style attributes that will change when setting a theme at runtime.
These attributes are nothing but colors for different React Native components. In a later section, using props from styled-components, you will learn how to extend the current styles of the HomeScreen component.
export const darkTheme = {
mode: 'dark',
PRIMARY_BACKGROUND_COLOR: '#353c51',
PRIMARY_TEXT_COLOR: '#767d92',
SECONDARY_TEXT_COLOR: '#ffffff',
PRIMARY_BUTTON_COLOR: '#152642',
SECONDARY_BUTTON_COLOR: '#506680'
}
export const lightTheme = {
mode: 'light',
PRIMARY_BACKGROUND_COLOR: '#ffefd5',
PRIMARY_TEXT_COLOR: '#DB7093',
SECONDARY_TEXT_COLOR: '#333333',
PRIMARY_BUTTON_COLOR: '#b9d6f3',
SECONDARY_BUTTON_COLOR: '#a1c9f1'
}
Adding Redux
To manage switching between two themes, we’ll use Redux. With the help of this state management library, you are going to create a store that will keep the initial value of a theme.
Redux will help to change the switch between two themes (defined in the previous section) at the runtime. This means you do not have to hardcode these values every time you want to add a new theme. Every time a theme is changed, the component or the screen will be re-rendered to display the new style attributes.
First, you will have to install the following libraries to create a store.
yarn add redux react-redux redux-thunk
Apart from Redux, the other two packages have important uses. React-redux lets your React Native components connect with the Redux store. Redux-Thunk is a middleware that enables you to make Redux actions return asynchronous operations. A thunk is a function that wraps an expression to delay its evaluation.
Creating Actions and Reducer
In Redux, the state of the whole application is represented by one JavaScript object. Think of this object as read-only, since you cannot make changes to its state (which is represented in the form of a tree) directly. That is what actions are for.
Actions are like events in Redux. They can be triggered by a user's touch on a button, key presses, timers, or network requests. The nature of each event mentioned is mutable. An action is a JavaScript object.
To define an action, there’s one requirement: each action has its type property. Every action needs a type of property to describe how the state should change.
Create a new folder called Redux at the root of your project. This directory is going to contain all the files related to Redux. To define an action, create a new file called action.js inside this folder.
There is only one action required right now called switchTheme. It will accept one parameter, the value of the theme.
// define type
export const SWITCH_THEME = 'SWITCH_THEME'
// dispatch actions
export const switchTheme = BaseTheme => {
return dispatch => {
dispatch({
type: SWITCH_THEME,
baseTheme: BaseTheme
})
}
}
To change the state of the app when using Redux. Or, in our case, to change the state of the value of the theme, dispatching the theme from the action switchTheme is the only way.
Next, let us define themeReducer, which will take the initial state of the application's theme and take action to change that theme.
import { lightTheme } from '../styles/theme'
import { SWITCH_THEME } from './actions'
const initialState = {
theme: { ...lightTheme }
}
const themeReducer = (state = initialState, action) => {
switch (action.type) {
case SWITCH_THEME:
let newState = {
...state,
theme: { ...state.theme, ...action.baseTheme }
}
return newState
default:
return state
}
}
export default themeReducer
A reducer is a pure function that calculates the next state based on the initial or previous state. It always produces the same output if the state is unchanged. In the above snippet, the current state of this application is the light theme. This theme will change whenever the user presses the button to switch it to the dark theme.
Creating a Store
To create the store, you will have to modify the App.js file. Start by adding the following import statements:
import React from 'react'
import { Provider } from 'react-redux'
import { createStore, applyMiddleware, combineReducers } from 'redux'
import thunk from 'redux-thunk'
import themeReducer from './redux/themeReducer'
import HomeScreen from './screens/HomeScreen'
A store is an object that brings actions and reducers together. It provides and holds state at the application level instead of individual components. Redux is not an opinionated library in terms of whether a framework or library should be used it or not.
Next, create the following store:
const store = createStore(
combineReducers({ themeReducer }),
applyMiddleware(thunk)
)
To bind a React Native application with Redux, you do it with the react-redux module. This is done by using the high-ordered component Provider. It passes the store down to the rest of the React Native application.
const App = () => {
return (
<Provider store={store}>
<HomeScreen />
</Provider>
)
Updating The HomeScreen Component
In this section, you are going to write the logic to consume the state from Redux's store as well as make use of ThemeProvider.
Styled-components gives React Native components theming support by a ThemeProvider wrapper component. In the render tree, all styled-components - such as Container, Title, and so on - will have access to the provided theme. Open the HomeScreen.js file and add the following import statements.
import styled, { ThemeProvider } from 'styled-components/native'
import { connect } from 'react-redux'
import { bindActionCreators } from 'redux'
import { switchTheme } from '../redux/actions'
import { darkTheme, lightTheme } from '../styles/theme'
In the above code snippet, do note that you are also importing both theme objects from the styles/theme.js file. This is necessary because, initially, you will have to pass a theme value for ThemeProvider to know and display the components accordingly. Then, the Redux action switchTheme (which is responsible for the theme change) expects a parameter of the current theme value.
Next, modify the render function inside the HomeScreen component. Wrap all of its previous contents inside the ThemeProvider wrapper and then add a new component called Button which will display the contents to change the current theme.
class HomeScreen extends React.Component {
render() {
return (
<ThemeProvider theme={this.props.theme}>
<Container>
<TextContainer>
<Title>Themed App with React Native & Styled Components</Title>
</TextContainer>
{this.props.theme.mode === 'light' ? (
<Button onPress={() => this.props.switchTheme(darkTheme)}>
<ButtonText>Switch to Dark Theme</ButtonText>
</Button>
) : (
<Button onPress={() => this.props.switchTheme(lightTheme)}>
<ButtonText>Switch to Light Theme</ButtonText>
</Button>
)}
</Container>
</ThemeProvider>
)
}
}
At this stage, you might be asking: how come this.props.theme & this.props.switchTheme are available to the above component? App.js, which is the parent component for HomeScreen, is not passing any props down the component tree.
Well, from the previous import statements, you are importing two important Redux methods: bindActionCreators and connect.
The bindActionCreators method maps actions to an object using the names of the action functions. These functions automatically dispatch the action to the store when the function is invoked. As we learned earlier, to change the data, we need to dispatch an action.
To enable this, you further need two things: mapStateToProps and mapDispatchToProps. You have to connect both of them with the HomeScreen component. This connection is done by using the connect() method from the react-redux package which connects the current React Native component to the Redux store.
Add the following at the end of the component file to connect the Redux store:
const mapStateToProps = state => ({
theme: state.themeReducer.theme
})
const mapDispatchToProps = dispatch => ({
switchTheme: bindActionCreators(switchTheme, dispatch)
})
export default connect(
mapStateToProps,
mapDispatchToProps
)(HomeScreen)
Bypassing an interpolated function ${props => props...} to a styled component's template literal, you can extend that component's styles. Take a look at the following code snippet and modify the styles wherever necessary.
const Container = styled.SafeAreaView`
flex: 1;
background-color: ${props => props.theme.PRIMARY_BACKGROUND_COLOR};
justify-content: center;
align-items: center;
`
const TextContainer = styled.View`
padding: 15px;
border-radius: 5px;
border: 1px solid ${props => props.theme.PRIMARY_TEXT_COLOR};
`
const Title = styled.Text`
padding: 20px;
font-size: 24px;
font-weight: 500;
color: ${props => props.theme.PRIMARY_TEXT_COLOR};
`
const Button = styled.TouchableOpacity`
margin-top: 20px;
background-color: ${props => props.theme.SECONDARY_BUTTON_COLOR};
border-radius: 5px;
padding: 10px;
`
const ButtonText = styled.Text`
font-size: 20px;
color: ${props => props.theme.SECONDARY_TEXT_COLOR};
`
Now, go to the simulator running, and you will notice a new button with text that says Switch to ... name of the next theme. If you have been following this tutorial, you will notice that the initial or current theme is the light mode.
By pressing the button, you can switch to dark mode.
Conclusion
Congratulations! You have successfully integrated Redux and styled-components into a React Native app to create style attributes for React Native and manage themes.
Using props in styled-components, you learned how to manage and write composable components. This is just one of the ways to create a themeable React Native app.
To delve more into styled-components, please refer to the official documentation.
If you are developing commercial React Native apps that contain sensitive logic, you can protect them against code theft, tampering, and reverse engineering.
Jscrambler
The leader in client-side Web security. With Jscrambler, JavaScript applications become self-defensive and capable of detecting and blocking client-side attacks like Magecart.
View All ArticlesMust read next
Create a React Native Image Recognition App with Google Vision API
In this tutorial, you'll learn how to use the Google Vision API to identify the content of images in a React Native mobile application.
February 13, 2019 | By Aman Mittal | 8 min read
Securing React Native Applications
React Native is the framework of choice for cross-platform mobile development. Here, we explore several strategies to secure React Native applications.
August 12, 2022 | By Jscrambler | 18 min read