October 6, 2022

Robotic Notes

All technology News

Theming React Native Applications with Styled Components

8 min read


Theming has become an essential part of mobile applications. Giving users the ability to switch between dark and light mode goes a long way in improving user experience and accessibility. In this article, we will be looking at how to implement dark mode in a mobile application using React native and Styled Components.

Styled components is CSS in JS solution for styling React and React native applications. This means you can write CSS in your JavaScript code rather than keeping styles in a separate stylesheet. Some of the advantages of this method are:

  • Eliminates classname bugs: As developers, sometimes we might misspell a class name or run into specificity issues due to duplicate styles. Styled components solve this by generating unique class names for your styles.
  • Automatic critical styles: Another disadvantage of traditional CSS is loading the entire stylesheet for each page opened, and component rendered even when some of the styles aren’t applied to elements in the page. Styled components only inject styles of components rendered which helps in improving performance
  • Simple dynamic styling: You can adapt the style of a component based on the props of a component without having to create separate styles.

Getting Started

We will be building a demo blog app with the Expo framework to show how we can use Styled components in a React native application. To get started, run the code below:

1npm install -global expo-cli

The above command installs the [expo-cli](https://docs.expo.dev/workflow/expo-cli/)a tool used in bootstrapping react native applications with Expo.

After installation, run expo init theming-mobile to create a react native application theming-mobile. An interactive prompt will show up to select a template, choose blank and press the enter / return key to continue the installation

January 16, 2022 9:23:47 pm - Screenshot

Run the code below after the installation to install the necessary libraries we will need for our demo blog app.

1expo install @react-native-async-storage/async-storage expo-linking

Once the process is complete, you’ve successfully bootstrapped an expo react-native application. To run the application, navigate to the project directory in a terminal and run the code expo start to start the expo development server. You will be presented with an interactive menu similar to the screenshot below

February 28, 2022 3:18:34 pm - Screenshot

The Expo dev server allows you to test your application locally while in development. You will need an emulator or the Expo Go App to run the app. The installation of an emulator is not covered in this article. Nevertheless, you can check here on how to install an emulator on your computer or the Expo Go app on your mobile device.

Assuming you have an emulator installed, press the relevant key that applies to the emulator, and it’ll run the app on the emulator.

Integrating Styled Components

We will be creating a Blog app to illustrate how to use styled-components. As previously explained, Styled-components is used in styling React native applications. To get started with styled-components, run the code below at the root of your project to install the styled-components library

1expo install styled-components

Next, create a style.js file in the root of your project directory and paste the code below.

1import styled from 'styled-components/native';

2import Constants from 'expo-constants';

3

4export const Container = styled.SafeAreaView`

5 background-color: #fff;

6 flex: 1;

7 align-items: center;

8 justify-content: center;

9 padding: 20px;

10 padding-top: ${Constants.statusBarHeight + 'px'};

11`;

12export const Header = styled.View`

13 display: flex;

14 width: 100%;

15 flex-direction: row;

16 justify-content: space-between;

17 align-items: center;

18 padding: 20px;

19`;

20export const ThemeButton = styled.Pressable`

21 padding: 10px;

22 border: 1px solid #000;

23`;

24export const ThemeButtonText = styled.Text`

25 font-size: 16px;

26 color: #000;

27`;

28export const TitleText = styled.Text`

29 font-weight: 600;

30 font-size: ${(props) => props.fontSize || '18px'};

31 color: #000;

32`;

33export const PostContainer = styled.View`

34 padding: 10px 20px;

35 width: 100%;

36`;

37export const PostText = styled.Text`

38 color: #73737d;

39 font-size: 16px;

40 padding: 10px 0 0;

41 font-weight: ${(props) => props.fontWeight || '400'};

42`;

We are using style.js to house all our styled components to be reused in other components. Styled components work by wrapping a react native component with custom styles in template literals.

1export const Container = styled.SafeAreaView`

2 background-color: #fff;

3 flex: 1;

4 align-items: center;

5 justify-content: center;

6 padding: 20px;

7 padding-top: ${Constants.statusBarHeight + 'px'};

8`;

In the example above, we create a styled component Container that wraps the react-native SafeAreaView component. We can also pass the components’ props and other valid JavaScript code as the styles are wrapped in template literals as you can see in the following example:

1export const PostText = styled.Text`

2 color: #73737d;

3 font-size: 16px;

4 padding: 10px 0 0;

5 font-weight: ${(props) => props.fontWeight || '400'};

6`;

We can complete the application now that we’ve created our styled components. Create a data.js file in the application directory and paste the code below

1export default [

2 {

3 id: 1,

4 body: 'Top 3 design patterns used to create React.JS components that you should know about',

5 title: '3 React Component Design Patterns You Should Know About',

6 date: '11th Jan 2022',

7 url: 'https://blog.openreplay.com/3-react-component-design-patterns-you-should-know-about',

8 },

9 {

10 id: 2,

11 body: 'Generate Github issues directly from the OpenReplay interface',

12 title: 'Integrating OpenReplay with GitHub',

13 date: '5th Nov 2021',

14 url: 'https://blog.openreplay.com/integrating-openreplay-with-github',

15 },

16 {

17 id: 3,

18 body: 'Learn how to create JIRA tickets directly from an OpenReplay session',

19 title: 'Integrating OpenReplay with Jira',

20 date: '24th Sept 2021',

21 url: 'https://blog.openreplay.com/integrating-openreplay-with-slack-in-a-web-application',

22 },

23];

This file will hold the mock blog data we will be using in our application. Create another file called PostItem.js file in your project directory and insert the code below

1import * as Linking from 'expo-linking';

2import { PostContainer, TitleText, PostText } from './style';

3const PostItem = ({ item }) => {

4 return (

5 <PostContainer>

6 <TitleText onPress={() => Linking.openURL(item.url)}>{item.title}</TitleText>

7 <PostText>{item.body}...</PostText>

8 <PostText fontWeight='600'>{item.date}</PostText>

9 </PostContainer>

10 );

11};

12export default PostItem;

The PostItem component will display a single blog post item that comprises the Blog title, body, and date the blog article was published. As you can notice, we aren’t using any React native component here directly but the styled-components we created.

Now open the App.js file and replace the code in the file with the code below

1import { useState, useEffect } from 'react';

2import { StatusBar } from 'expo-status-bar';

3import { FlatList } from 'react-native';

4import PostItem from './PostItem';

5import data from './data';

6import { Container, Header, ThemeButton, ThemeButtonText, TitleText } from './style';

7export default function App() {

8 return (

9 <Container>

10 <Header>

11 <TitleText fontSize='24px'>Blog</TitleText>

12 <ThemeButton>

13 <ThemeButtonText onPress={() => null}>

14 Dark Mode

15 </ThemeButtonText>

16 </ThemeButton>

17 </Header>

18 <FlatList data={data} renderItem={PostItem} keyExtractor={(item) => item.id} />

19 <StatusBar style='auto' />

20 </Container>

21 );

22}

Like what was done in the PostItem.js component, we use our styled components to replace the default components. We also introduced a FlatList component to render the mock data from the data.js file in the PostItem component.

Save and reload your emulator. You should get something similar to the screenshot below.

null

Open Source Session Replay

Debugging a web application in production may be challenging and time-consuming. OpenReplay is an Open-source alternative to FullStory, LogRocket and Hotjar. It allows you to monitor and replay everything your users do and shows how your app behaves for every issue. It’s like having your browser’s inspector open while looking over your user’s shoulder. OpenReplay is the only open-source alternative currently available.

OpenReplay

Happy debugging, for modern frontend teams – Start monitoring your web app for free.

Theming

Now that we have our application up and running, the next step is applying the themes. We already have a default light mode theme. We will implement a dark mode theme in this section. Create a theme.js file and paste the code below

1const darkTheme = {

2 PRIMARY_COLOR: '#000',

3 SECONDARY_COLOR: '#73737d',

4 TITLE_COLOR: '#fff',

5 BACKGROUND_COLOR: '#111216',

6 BUTTON_COLOR: '#fff',

7};

8const lightTheme = {

9 PRIMARY_COLOR: '#fff',

10 SECONDARY_COLOR: '#73737d',

11 TITLE_COLOR: '#000',

12 BACKGROUND_COLOR: '#fff',

13 BUTTON_COLOR: '#000',

14};

15export { lightTheme, darkTheme };

This file contains the theme colors we will be using in the application. Now go back to the style.js code and replace the code with the code below

1import styled from 'styled-components/native';

2import Constants from 'expo-constants';

3export const Container = styled.SafeAreaView`

4 background-color: ${(props) => props.theme['PRIMARY_COLOR']};

5 flex: 1;

6 align-items: center;

7 justify-content: center;

8 padding: 20px;

9 padding-top: ${Constants.statusBarHeight + 'px'};

10`;

11export const Header = styled.View`

12 display: flex;

13 width: 100%;

14 flex-direction: row;

15 justify-content: space-between;

16 align-items: center;

17 padding: 20px;

18`;

19export const ThemeButton = styled.Pressable`

20 padding: 10px;

21 border: 1px solid ${(props) => props.theme['BUTTON_COLOR']};

22`;

23export const ThemeButtonText = styled.Text`

24 font-size: 16px;

25 color: ${(props) => props.theme['BUTTON_COLOR']};

26`;

27export const TitleText = styled.Text`

28 font-weight: 600;

29 font-size: ${(props) => props.fontSize || '18px'};

30 color: ${(props) => props.theme['TITLE_COLOR']};

31`;

32export const PostContainer = styled.View`

33 padding: 10px 20px;

34 width: 100%;

35`;

36export const PostText = styled.Text`

37 color: ${(props) => props.theme['SECONDARY_COLOR']};

38 font-size: 16px;

39 padding: 10px 0 0;

40 font-weight: ${(props) => props.fontWeight || '400'};

41`;

We’ve replaced the colors with props that will be passed down depending on the theme selected, so if a user selects dark mode, we’ll apply the colors from the darkMode theme in the theme.js folder to the styled-components.

Now go to the App.js file and paste the code below.

1

2import { ThemeProvider } from 'styled-components/native';

3import { darkTheme, lightTheme } from './theme';

4

5export default function App() {

6 const [theme, setTheme] = useState('light');

7 const toggleTheme = async () => {

8 const themeValue = theme === 'dark' ? 'light' : 'dark';

9 setTheme(themeValue);

10 };

11 return (

12 <ThemeProvider theme={theme === 'dark' ? darkTheme : lightTheme}>

13 <Container>

14 <Header>

15 <TitleText fontSize='24px'>Blog</TitleText>

16 <ThemeButton>

17 <ThemeButtonText onPress={() => toggleTheme()}>

18 {theme === 'dark' ? 'Light' : 'Dark'} Mode

19 </ThemeButtonText>

20 </ThemeButton>

21 </Header>

22 <FlatList data={data} renderItem={PostItem} keyExtractor={(item) => item.id} />

23 <StatusBar style='auto' />

24 </Container>

25 </ThemeProvider>

26 );

27}

Styled components have theming support by default which is available with the ThemeProvider component based on React context. The ThemeProvider component allows us to pass data through the component tree without drilling props down through every component level.

1<ThemeProvider theme={theme === 'dark' ? darkTheme : lightTheme}>

We pass the theme value down, which returns a theme object that depends on the state, so when a user toggles the theme, we update the theme state with the correct theme value and pass down the colors of the chosen theme to the styled-components. Reload the app, and now when you click the Dark mode button, it should switch the theme

Persisting the theme

We have now successfully created two themes for the application: light mode and dark mode, which is the aim of this article, but there is a small problem. If you reload the app, you’ll notice the theme resets back to light mode even after switching to dark mode.

To fix that, we will be using a React native library called Async Storage which provides a persistent storage that we can use to store string data. The idea is to store the current theme so that we can fetch the stored theme when the app is loaded and apply it to the application.

Go to your App.js file and insert the code below:

1

2import AsyncStorage from '@react-native-async-storage/async-storage';

3

4export default function App() {

5 const [theme, setTheme] = useState('light');

6 useEffect(() => {

7 getTheme();

8 }, []);

9 const getTheme = async () => {

10 try {

11 const themeValue = await AsyncStorage.getItem('@theme');

12 if (themeValue) setTheme(themeValue);

13 } catch (error) {

14 console.log(error);

15 }

16 };

17 const toggleTheme = async () => {

18 const themeValue = theme === 'dark' ? 'light' : 'dark';

19 try {

20 await AsyncStorage.setItem('@theme', themeValue);

21 setTheme(themeValue);

22 } catch (error) {

23 console.log(error);

24 }

25 };

26 return(

27

28 )

29}

There are two functions to note in the code above the getTheme and toggleTheme functions. When the app loads for the first time, we check if there is an existing theme in the Async storage. If there is, we replace the default theme in the state with the existing one. If a user toggles the theme, we store the current theme in the Async storage and the theme state.

Now refresh your app again. You’ll notice even when you quit and open the app again. It still uses the last saved theme.

Conclusion

At this point, you should be able to implement theming in your mobile applications. You could do so much more with Styled components, such as creating a design system and extending the styled-components. Please endeavor to check the Styled component docs. You can also try extending the app we created to include other themes. I’d love to see what you come up with. The complete code for this article is available here.



Source link