FullStack Labs

Please Upgrade Your Browser.

Unfortunately, Internet Explorer is an outdated browser and we do not currently support it. To have the best browsing experience, please upgrade to Microsoft Edge, Google Chrome or Safari.
Upgrade

How to Create a Box Component in React Native with Styled System

Written by 
Felipe Moyano
,
Senior Software Engineer
How to Create a Box Component in React Native with Styled System
blog post background
Debugging in React Native: Flipper vs React Native Debugger vs Reactotron
Agile Test Management - Zephyr for JIRA
The Difference Between MongoDB and Firestore, and When You Should Use Each

Enforce consistent theme use throughout your application with a reusable Box component.

What is a theme?

A theme is a series of configurable design constraints such as colors, typographies, font sizes, font weights, spacing scale, etc.

Why is a theme important?

When it comes to design, consistency is key to making a final product that looks polished. Sharing the same color palette, spacing between elements, and breakpoints across all components goes a long way in creating a pleasant UI. You can, of course, achieve this without implementing a theme, but being constrained by your theme will make it harder to deviate from it. It will also make it easier to make decisions as you build. Instead of asking yourself if a support text looks better at 15px or 16px, you simply stick to your scale. In that case, it’s either a small text or a regular text. 

The Box component

The box component is the basic building block to implement our theme in our application. We can think of it as a `View` with special style props that will receive the keys on the theme object and map them to their corresponding values.

Let’s start by creating a new React Native app with expo-cli:

	
expo init BoxApp
	

Follow the instructions on your console and pick the blank template under managed workflow.

After the installation completes, switch to your project’s folder and run

	
yarn add styled-system @emotion/core @emotion/native emotion-theming
	

Now let’s add a theme file for our application:

	
/* theme.js */
export default {
 space: [0, 4, 8, 16, 32, 64, 128, 256, 512],
 fontSizes: {
   xs: 12,
   sm: 14,
   md: 16,
   lg: 24,
   xl: 32,
 },
 fontWeights: {
   body: "400",
   heading: "700",
   bold: "900",
 },
 lineHeights: {
   body: 1.5,
   heading: 1.125,
 },
 colors: {
   text: "#1F2933",
   white: "#fff",
   background: "#E4E7EB",
   primary: "#07c",
   secondary: "#30c",
   muted: "#f6f6f6",
 },
 shadows: {
   sm: {
     shadowColor: "#000",
     shadowOffset: {
       width: 0,
       height: 1,
     },
     shadowOpacity: 0.2,
     shadowRadius: 1.41,

     elevation: 2,
   },

   md: {
     shadowColor: "#000",
     shadowOffset: {
       width: 0,
       height: 2,
     },
     shadowOpacity: 0.23,
     shadowRadius: 2.62,

     elevation: 4,
   },
 },
};
	

We’ll be using the Styled System library to build our Box. Styled System provides a collection of utility functions that allows us to map styles to component properties based on a global theme object.

Let’s go ahead and create a file, system.js under src/utils to put some of the boilerplate necessary to create our components:

	
/* utils/system.js */
import {
 space,
 color,
 layout,
 flexbox,
 border,
 position,
 shadow,
 compose,
} from "styled-system";

export const viewProps = compose(
 layout,
 color,
 space,
 border,
 position,
 shadow,
 flexbox
);
	

Now we can create a Box.js file under a components directory:

-- CODE language-jsx keep-markup --
/* components/Box.js */

import
React from "react";

import
styled from "@emotion/native";

import
{ viewProps } from "../utils/system";

import
{ useTheme } from "emotion-theming";

const
StyledBox = styled.View(viewProps);

/* Since shadows in React Native consist of multiple properties,
we include this function to help us map them from one key to
multiple values. */


functionmapShadowToStyle
(key, theme) {

const
_obj = theme.shadows[key];

if
(!_obj) return {};

return
_obj;

}

exportconst
Box = React.forwardRef((props, ref) => {

const
{ shadow, style, children, ...rest } = props;

const
theme = useTheme();

const
_shadow = shadow && mapShadowToStyle(shadow, theme);

return
(

  <StyledBox ref={ref} {...rest} style={[style, { ..._shadow }]}>

    {children}

  </StyledBox>

  );

});

Box.displayName = "Box";

Great, now let’s go back to our App.js and start using our Box component. But first, we have to make sure to include a ThemeProvider so that Styled System can pull in our global theme and apply it to the Box. Once we’ve done that we can put our newly created Box component into action:

-- CODE language-jsx keep-markup --

import
{ StatusBar } from "expo-status-bar";

import
React from "react";

import
{ Text } from "react-native";

import
{ ThemeProvider } from "emotion-theming";

import
theme from "./src/theme";

import
{ Box } from "./src/components/Box";

export
default function App() {

  return (

    <ThemeProvider theme={theme}>

      <Box flex={1} alignItems="center" backgroundColor="background">

        <Box marginTop={6} padding={4} backgroundColor="primary" shadow="md">

          <Text style={{ color: "white" }}>Hello from a box component!</Text>

        </Box>

      </Box>

      <StatusBar style="auto" />

    </ThemeProvider>

  );

}

Notice that the numerical values we’re using on the margin and padding properties don’t map to pixels, but instead use the values defined previously on our space scale:  

-- CODE language-jsx keep-markup --
space: [0, 4, 8, 16, 32, 64, 128, 256, 512]

We can also make use of our color palette and reference it directly on the backgroundColor properties and even use the shadows on our theme! 

What about text?

It would certainly be nice if in the example above we could also use our theme to render the message. Since React Native requires us to place text inside Text components (unlike web browsers, where any element can have inner text), we can’t use our Box to replace Text components.

Personally, I don’t find it as useful to define a general purpose text component. Instead, I prefer defining typographic components that have a clear purpose, such as Heading, Content, etc.

Let’s look at a possible implementation for a Heading component:

-- CODE language-jsx keep-markup --

import
styled from "@emotion/native";import React from "react";

import
{
  typography,
  space,
  color,
  layout,
  flexbox,
  border,
  fontSize,
  fontWeight,
  compose,
} from "styled-system";

exportconst
textProps = compose(
  typography,
  space,
  color,
  layout,
  flexbox,
  border
);


exportconst
StyledText = styled.Text(textProps);


exportfunctionHeading
({
  color = "text",
  size = "xl",
  fontWeight = "heading",
  ...props
}) {

return
(
    <StyledText
      color={color}
      fontWeight={fontWeight}
      fontSize={size}
      {...props}
    />
  );
}

Heading.displayName = "Heading";

Great, now we have a Heading with some useful defaults that we can reuse in our application. Let’s go back to our App.js and include this Heading component:

-- CODE language-jsx keep-markup --

/* App.js */


... 

<Box flex={1} alignItems="center" backgroundColor="background">
  
<Heading mt={4}>I'm a Heading!</Heading>
    
  <Box marginTop={6} padding={4} backgroundColor="primary" shadow="md">
    
    <Text style={{ color: "white" }}>Hello from a box component!</Text>

  </Box>

</Box>
...

Going back to our app, we can see it in action:

Conclusion

These two components will give us a solid foundation to build on our theme and implement a design system throughout our application. From here, we could reuse them to implement layout primitives such as a Stack, Inline, or Grid, and typography components such as SubHeading, Content or TextLink. The theme object can also be extended with your brand colors, new shadows and much more. Hopefully you can use this foundation to create awesome components in your React Native app!

Using techniques like what is listed above, we have had the opportunity to address clients’ concerns and they love it! If you are interested in joining our team, please visit our Careers page.

Felipe Moyano
Written by
Felipe Moyano
Felipe Moyano


I love building things, solving problems, and creating value for people — and working in software development allows me to do that every day. I like to always challenge myself and learn new things, whether I'm building complex back-end solutions for Movistar, acting as the CTO of a fitness platform, or developing educational platforms so others can learn themselves. My favorite technology is Ruby on Rails for the way it empowers small teams to build great tools. I'm easygoing, committed, and cheerful, and I love longboarding and playing tennis.

FullStack Labs Icon

Let's Talk!

We’d love to learn more about your project. Contact us below for a free consultation with our CEO.
Projects start at $50,000.

company name
name
email
phone
Type of project
Reason for contact
How did you hear about us?
Thank you! Your submission has been received!
Oops! Something went wrong while submitting the form.