Sharing Code Between React and React Native

How to share common code and design elements as dependencies for your web and mobile applications.

Jonathan Saring
ITNEXT

--

The React-Native accordion we’ll build; We’ll have a React one as well for the web

Based on this wonderful guide by Jony Kruszewski

React and React Native help bring web and mobile development closer together to improve both user and developer experiences.

Some teams go as far as building a full cross-platform Design System with React and React Native (video tutorial).

While React and RN require a different implementation (browsers and mobile devices are not the same), both types of apps and components often have common code , business logic, hooks, props and even design tokens.

In this tutorial I’ll show you how to turn common elements into shared dependencies for both React and RN applications. These components are versioned and we can later update and manage them across platforms.

That’s made simple using open-source project Bit.

Here’s what we will do:

  1. Two Components: React and React Native
  2. Turning common code and design tokens into shared dependencies
  3. Adding them to platform-specific components
  4. Providing themes to React and RN
  5. Creating web and a mobile apps with shared dependencies

Let’s dive in.

Here you can find (and use) all the components in this tutorial.

1. Two components: React and React Native

In this tutorial s we will work with two accordions, one for React and another for React Native. This is what they look like:

One is a React accordion component. Explore it and install/fork it to play around with it locally or edit it.

The other is the React Native component:

Take a second to explore the “dependencies” graph for each component. You will notice they share many dependencies in common.

Shared dependencies between React and React Native components

2. Turning common code into shared dependencies

We want each accordion to contain the minimum amount of platform-specific code (since their APIs are different) and will outsource all the common parts.

With Bit, we can turn anything into components and therefore dependencies for out accordion components. Ee want to have as much code outside the platform-specific implementations as possible, meaning that hooks, types, themes, and design tokens will be part of different components.

Here are component dependencies we want to create and compose with our React and React Native accordions (you can find all of them here):

share-react-react-native              --> scope
├── api --> namespace
│ ├── accordion --> component
│ └── accordion-items --> component
├── design-tokens --> namespace
│ ├── base-tokens --> component
│ ├── react-tokens --> component
│ └── rnative-tokens --> component
├── base-ui --> namespace
│ ├── hooks --> namespace
│ │ ├── use-open --> component
│ │ └── use-select --> component
│ ├── react --> namespace
│ │ └── accordion --> component
│ └── react-native --> namespace
│ └── accordion --> component
└── theme --> namespace
├── web --> component
└── mobile --> component

3. Adding them to the platform-specific components

There are differences between React and React Native. For example, they don’t allow the same styling properties and types, and while all dimensions in React Native are unitless, in React you can use px, rem, em etc.

To make this work we will have a base object that shares the most common values and other Platform specific components that add properties.

We will apply the useful pattern of creating a context and a hook to inject our tokens into our components. The trick to making it work with React and React Native is that we are going to have three kinds of tokens:

  • A base Token - common to both:
export interface BaseTokensProps {
primaryColor: string;
secondaryColor: string;
borderColor: string;
borderStyle: "solid" | "dotted" | "dashed" | undefined;
}

export const baseTokens: BaseTokensProps = {
primaryColor: "red",
secondaryColor: "blue",
borderColor: "green",
borderStyle: "solid",
};
  • A Token extended from the base one to use with React:
import { baseTokens } from "@learnbit-react/web-mobile-design-system.design-tokens.base-tokens";
import type { BaseTokensProps } from "@learnbit-react/web-mobile-design-system.design-tokens.base-tokens";

export interface ReactTokensProps extends BaseTokensProps {
spacing: string;
fontSize: string;
borderWidth: string;
}

export const reactTokens: ReactTokensProps = {
...baseTokens,
spacing: "15px",
fontSize: "18px",
borderWidth: "3px",
};
  • A Token extended from the base one to use with React Native:
import { baseTokens } from "@learnbit-react/web-mobile-design-system.design-tokens.base-tokens";
import type { BaseTokensProps } from "@learnbit-react/web-mobile-design-system.design-tokens.base-tokens";

export interface RNativeTokensProps extends BaseTokensProps {
spacing: number;
fontSize: number;
borderWidth: number;
}

export const rNativeTokens: RNativeTokensProps = {
...baseTokens,
primaryColor: "purple",
secondaryColor: "gray",
spacing: 10,
fontSize: 12,
borderWidth: 3,
};

If your IDE autocompletes the location using relative imports, you can quickly fix it by running `bit link — rewire`

4. Providing themes to React and React Native

With Bit a theme can be a component too — which is amazing here because we can easily compose dependencies for design tokens and thems.

For our theme, we will use a pre-existing component that creates a Theme from an object. For that, we will need to install it in our workspace:

$ bit install @teambit/base-react.theme.theme-provider

We need tan object with the theme properties and call the createTheme function, providing it as an argument. The result will be an object with a hook to use those values and a component that injects it using the React Context.

import { createTheme } from "@teambit/base-react.theme.theme-provider";

import { reactTokens } from "@learnbit-react/web-mobile-design-system.design-tokens.react-tokens";
import type { ReactTokensProps } from "@learnbit-react/web-mobile-design-system.design-tokens.react-tokens";

const theme = createTheme<ReactTokensProps>({
theme: reactTokens,
});

const { useTheme, ThemeProvider } = theme;
export { useTheme, ThemeProvider };
  • And here’s the theme for React Native. We’ll to pass the option withoutCssVars: true to avoid having to render a <div>.
import { createTheme } from "@teambit/base-react.theme.theme-provider";

import { rNativeTokens } from "@learnbit-react/web-mobile-design-system.design-tokens.rnative-tokens";
import type { RNativeTokensProps } from "@learnbit-react/web-mobile-design-system.design-tokens.rnative-tokens";

const theme = createTheme<RNativeTokensProps>({
theme: rNativeTokens,
withoutCssVars: true,
});

const { useTheme, ThemeProvider } = theme;

export { useTheme, ThemeProvider };

Now let’s provide them to the Accordions.

That’s simple now, we can just import the Theme components.

Both of our Accordions will have the same skeleton, where we consume the hooks and return a mapped list, as seen in the abstraction here:

import { useTheme } from '@learnbit-react/web-mobile-design-system.theme.web // or .mobile in the react-native one!
import { useOpen } from '@learnbit-react/web-mobile-design-system.hooks.use-open';
import { useSelect } from '@learnbit-react/web-mobile-design-system.hooks.use-select';
import type { AccordionProps } from '@learnbit-react/web-mobile-design-system.api.accordion';

const GenericAccordionTemplate = ({ elementList } : AccordionProps) => {
const { isOpen, toggleOpen } = useOpen();
const { selectedId, setSelection } = useSelect();
const {someValueToken, someValueToken} = useTheme();

return <div_or_View style={{someProp: someValueToken}}>My styled element<div_or_View/>;
};

Note that the two accordions don’t use the same useTheme hook. Each one uses a different one to comply with the proper styling types.

5. Creating web and mobile apps with shared dependencies

Finally, let’s build web and mobile apps using our new components and shared dependencies. In composable apps (i.e. with Bit) you can add an app component and deploy it. Since everything is a component, we can add a netlify-deployer component, or create/add any similar components to deploy anywhere. Learn more here.

Let’s start with the React web app

Here’s the final result deployed to Netlify. Let’s see how it’s done.

Let’s create an app component and install the Netlify deployer.

$ bit create react-app apps/react/accordion — scope learnbit-react.web-mobile-design-systembit install react-router-dombit use learnbit-react.web-mobile-design-system/apps/react/accordionbit install @teambit/cloud-providers.deployers.netlify

Then add the accordion to the app’s code.

import React from "react"; 
import { Routes, Route } from "react-router-dom";
import { Accordion } from "@learnbit-react/web-mobile-design-system.base-ui.react.accordion";
import { Item } from "@learnbit-react/web-mobile-design-system.api.accordion";
export function AccordionApp() {
return (
<>
{/* header component */}
<Routes>
<Route
path="/"
element={
<Accordion
elementList={[
new Item("Asia", "01").toObject(),
new Item("Africa", "02").toObject(),
new Item("North America", "03").toObject(),
new Item("South America", "04").toObject(),
new Item("Antarctica", "05").toObject(),
new Item("Australia / Oceania", "06").toObject(),
new Item("Europe", "07").toObject(),
]}
/>
}
/>
<Route path="/about">{/* about page component */}</Route>
</Routes>
{/* footer component */}
</>
);
}

And configuring the app in accordion.react-app.ts, runing bit tag will snapshot it and also deploy it:

bit tag apps/react/accordion -m “First deploy”

And it’s deployed!

Let’s consume the React Native component in Expo

expo init my-new-project cd my-new-project yarn install @learnbit-react/web-mobile-design-system.base-ui.react-native.accordion @learnbit-react/web-mobile-design-system.api.accordion

Add the component to the app.js file”

import {Accordion} from '@learnbit-react/web-mobile-design-system.base-ui.react-native.accordion';
import { Item } from '@learnbit-react/web-mobile-design-system.api.accordion';

import {(StyleSheet, View)} from 'react-native';

export default function App() {
return (
<View style={styles.container}>
<Accordion
elementList={[
new Item('Asia', '01').toObject(),
new Item('Africa', '02').toObject(),
new Item('North America', '03').toObject(),
new Item('South America', '04').toObject(),
new Item('Antarctica', '05').toObject(),
new Item('Australia / Oceania', '06').toObject(),
new Item('Europe', '07').toObject(),
]}
/>
</View>
);
}

const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'flex-start',
marginTop: 50,
},
});

Here’s the source-code on GitHub too :)

Summary

Browsers and mobile devices are different and you can’t use everything in common between them. However, there are a lot of code and design elements that can be common. By turning these elements into composable dependencies we can effectively share them and compose them with our React and React Native components and applications. We can version and manage updates between them to keep our codebase and our UI/UX consistent across platforms.

I hope you enjoyed and found it useful, feel free to check out and use the examples in this guide, start working with Bit to turn more things into components (from UI elements to pages, experiences, apps and even backend) and have a great time building in React and React Native.

Cheers 🍻

--

--

I write code and words · Component-driven Software · Micro Frontends · Design Systems · Pizza 🍕 Building open source @ bit.dev