Edmund Ekott Frontend engineer who specializes in building complex UIs with JavaScript and CSS.

Using TypeScript with React Native

7 min read 2046

There’s no doubt that TypeScript has become a favorite for many developers, and with over 19 million downloads weekly on npm, there’s a chance you’ll see Typescript in two out of every three JavaScript-related job descriptions.

In this article, we’ll be looking at how to use TypeScript in your React Native applications. By the end of this article, you’ll be comfortable enough to take advantage of the benefits of TypeScript to minimize errors and add type checking to your application.

What is TypeScript?

TypeScript is an open source language built on top of JavaScript by Microsoft. You can think of TypeScript as JavaScript, but with static type definitions.

Why you should consider TypeScript

If the first two paragraphs of this article aren’t convincing enough to get you to try out TypeScript, here are a few more reasons:

  • Better documentation: With clearly defined types, you can easily know how to reference other parts of your code
  • Error reduction: TypeScript can validate your code before execution, therefore saving you loads of time debugging why “undefined is not a function at line 23” 💀
  • Compiles to JavaScript: Because TypeScript is a superset of JavaScript, it compiles to JavaScript, and all valid JavaScript is valid TypeScript
  • Easy adoption: If you’ve ever wanted to add TypeScript to your existing JavaScript applications, you can easily start with one file and see how it goes from there

Building an application with React Native and TypeScript

Now that we know what TypeScript is, we can start looking at how to use it in our React Native applications.

In this tutorial, we’re going to be building a shopping list application. This is a great way to see how TypeScript can improve your developer experience without things getting too complex.

Requirements

  • Basic knowledge of running CLI commands
  • Node.js and npm installed on your machine
  • XCode or Android Studio installed on your machine
  • Basic knowledge of JavaScript
  • Basic knowledge of React
  • Some experience with React Native (suggested, not required)

Getting started

Let’s get started, shall we? Run the command below in your terminal:

npx react-native init ShoppingList --template react-native-template-typescript

The command above will set up a new React Native project using a TypeScript template with the required dependencies and configurations in a folder called ShoppingList.

To learn how you can use TypeScript in an existing application, take a look at the docs.

Building the base UI

Open the folder in your code editor and run the commands below (depending on your machine) to start the project:

We made a custom demo for .
No really. Click here to check it out.

# MacOS
npm run ios

# Windows/Linux
npm run android

The App.tsx component is the root component in our app. This component will contain other components we use in the app, and we’ll get back to it in a bit. For now, let’s create our first component.

Create a folder structure like src/components in the root directory and create a Header.tsx component in the components folder.

Do you notice that the files we’re creating have a .tsx extension? That’s because we’re now building with TypeScript and we need to use the proper file extensions, otherwise we’ll get errors in the regular .js files.

Paste the code below into the file:

import React from 'react';
import {View, Text, StyleSheet} from 'react-native';
interface Props {
  title: string;
}
const Header: React.FC<Props> = ({title}) => {
  return (
    <View style={styles.header}>
      <Text style={styles.headerText}>{title}</Text>
    </View>
  );
};
const styles = StyleSheet.create({
  header: {
    paddingVertical: 20,
    borderBottomWidth: 1,
    borderBottomColor: '#cfcfcf',
    alignItems: 'center',
  },
  headerText: {
    fontSize: 20,
    fontWeight: '600',
  },
});
export default Header;

If you’re already familiar with React Native, ignore most of the code above; you’ll know what we’re trying to do here. However, I want to draw your attention to lines 3-6:

interface Props {
  title: string;
}
const Header: React.FC<Props> = ({title}) => { /* content here*/}

Because we’re using TypeScript, we are now able to define what our component should take in, how it should take it in, and how many to take.

In the first three lines, we declared an interface, which serves as the structure for the props object we’d normally access in our component. However, this time we’re specifying the props and their types using TypeScript.

The benefit of doing it this way is that we get better IntelliSense and some validation when we’ll use the component ( e.g. when the title is not passed to the component, there’s going to be an instant error).

Back in the App.tsx component, replace the content with the code below:

import React from 'react';
import {SafeAreaView, StyleSheet, Text, View} from 'react-native';
import Header from './src/components/Header';
const App = () => {
  return (
    <SafeAreaView style={styles.container}>
      <Header />
    </SafeAreaView>
  );
};
const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#e8e7e3',
  },
});
export default App;

You should notice a red line under the <Header/> component. If you hover over it, you should see a message that the component expects a title prop that was not supplied. Let’s do that now.

Replace that line with the code snippet below and you should see that the error is gone:

<Header title="Shopping List" />

If you tried to assign a number (or any other data type that isn’t a string) you’ll get a different error. This is TypeScript trying to help you catch bugs before they happen.

Adding items to the list

Create a new component, AddItem.tsx, to your /src/components folder and paste the code below:

import React, {useState} from 'react';
import {
  View,
  Text,
  TextInput,
  TouchableOpacity,
  StyleSheet,
} from 'react-native';
export interface IItem {
  item: string;
  quantity: string;
}
const AddItem = () => {
  const [item, setItem] = useState('');
  const [quantity, setQuantity] = useState('');
  return (
    <View>
      <Text style={styles.heading}>Add Shopping Item</Text>
      <View style={styles.form}>
        <TextInput
          style={styles.input}
          placeholder="Enter item"
          value={item}
          onChangeText={text => setItem(text)}
        />
        <TextInput
          style={styles.input}
          placeholder="Enter quantity"
          keyboardType="numeric"
          value={quantity}
          onChangeText={q => {
            setQuantity(q);
          }}
        />
        <TouchableOpacity style={styles.addItemButton} onPress={() => {}}>
          <Text style={styles.buttonText}>Add Item</Text>
        </TouchableOpacity>
      </View>
    </View>
  );
};
const styles = StyleSheet.create({
  heading: {
    fontSize: 20,
    fontWeight: '700',
  },
  form: {
    marginTop: 30,
  },
  input: {
    padding: 15,
    borderColor: 'rgba(0, 0, 0, 0.2)',
    borderWidth: 1,
    borderRadius: 5,
    marginBottom: 20,
  },
  addItemButton: {
    backgroundColor: '#eb8634',
    paddingVertical: 20,
    borderRadius: 5,
    alignItems: 'center',
  },
  buttonText: {color: '#fff', fontWeight: '500'},
});
export default AddItem;

Notice the named export IItem on line 9? That’s the structure of an item in our shopping list, and we export it because we’ll be needing it in other components.

Back to App.tsx, update the component with the code below:

import React, {useState} from 'react';
import {SafeAreaView, StyleSheet, Text, View} from 'react-native';
import Header from './src/components/Header';
import AddItem, {IItem} from './src/components/AddItem'; /* import AddItem and interface*/
const App = () => {
  const [shoppingList, setShoppingList] = useState<IItem[]>([]); // set the type of what the hook expects to be an array of IItems.
  return (
    <SafeAreaView style={styles.container}>
      <Header title="Shopping List" />
      <View style={styles.contentWrapper}>
        <AddItem
          setShoppingList={setShoppingList}
          shoppingList={shoppingList}
        />
      </View>
    </SafeAreaView>
  );
};
const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#e8e7e3',
  },
  contentWrapper: {
    padding: 20,
  },
});
export default App;

Now in App.tsx we imported the new AddItem component and the IItem interface so we can use (on line 6) the useState hook to create the shoppingList state.

We specified that the hook should accept an array of IItem when using the setShoppingList function. If you hover on the setShoppingList function, you’ll see the type. Take note of it, we’ll be needing it soon when we add items to the shopping list in AddItem.tsx.

Go back to the AddItem.tsx component and update it with this:

import React, {useState} from 'react';
import {
  View,
  Text,
  TextInput,
  TouchableOpacity,
  StyleSheet,
  Alert,
} from 'react-native';
export interface IItem {
  item: string;
  quantity: string;
}
interface Props {
  setShoppingList: React.Dispatch<React.SetStateAction<IItem[]>>;
  shoppingList: IItem[];
}
const AddItem: React.FC<Props> = ({shoppingList, setShoppingList}) => {
  const [item, setItem] = useState('');
  const [quantity, setQuantity] = useState('');
  const addItem = () => {
    if (!item) {
      Alert.alert('No Item!', 'You need to enter an item');
    } else {
      setShoppingList([
        ...shoppingList,
        {
          item,
          quantity: quantity || '1',
        },
      ]);
      setItem('');
      setQuantity('');
    }
  };
  return (
    <View>
      <Text style={styles.heading}>Add Shopping Item</Text>
      <View style={styles.form}>
        <TextInput
          style={styles.input}
          placeholder="Enter item"
          value={item}
          onChangeText={text => setItem(text)}
        />
        <TextInput
          style={styles.input}
          placeholder="Enter quantity"
          keyboardType="numeric"
          value={quantity}
          onChangeText={q => {
            setQuantity(q);
          }}
        />
        <TouchableOpacity style={styles.addItemButton} onPress={addItem}>
          <Text style={styles.buttonText}>Add Item</Text>
        </TouchableOpacity>
      </View>
    </View>
  );
};
const styles = StyleSheet.create({
  heading: {
    fontSize: 20,
    fontWeight: '700',
  },
  form: {
    marginTop: 30,
  },
  input: {
    padding: 15,
    borderColor: 'rgba(0, 0, 0, 0.2)',
    borderWidth: 1,
    borderRadius: 5,
    marginBottom: 20,
  },
  addItemButton: {
    backgroundColor: '#eb8634',
    paddingVertical: 20,
    borderRadius: 5,
    alignItems: 'center',
  },
  buttonText: {color: '#fff', fontWeight: '500'},
});
export default AddItem;

There are significant changes in the component now, so I’ll walk you through them.

On line 14, we define the Props interface and set the type of each property, and then set it as the type of props in our functional component on line 18.

We also created a handler function on line 21, when the “Add Item” button is clicked, the function checks if an item was entered in the field, then invokes the setShoppingList() function to add a new item to the list.

Listing out items

Now that we’re able to add new items, let’s create a component to list them out.

Create another file in /src/components named Item.tsx and paste the code below inside:

import React from 'react';
import {View, Text, StyleSheet} from 'react-native';
import {IItem} from './AddItem';
const Item: React.FC<IItem> = ({item, quantity}) => {
  return (
    <View style={styles.item}>
      <Text style={styles.itemName}>{item}</Text>
      <Text style={styles.quantity}>x{quantity}</Text>
    </View>
  );
};
const styles = StyleSheet.create({
  item: {
    padding: 20,
    flexDirection: 'row',
    justifyContent: 'space-between',
    alignItems: 'center',
    borderBottomWidth: 1,
    borderBottomColor: 'rgba(0, 0, 0, 0.2)',
  },
  itemName: {
    fontWeight: '500',
  },
  quantity: {
    padding: 6,
    borderWidth: 1,
    borderColor: 'rgba(0, 0, 0, 0.2)',
    borderRadius: 10,
    overflow: 'hidden',
    backgroundColor: 'rgba(0, 0, 0, 0.05)',
  },
});
export default Item;

You probably get the hang of it already! The Item.tsx component accepts properties from IItem as props, and then we render them with some styling.

Now we need to finish up the app by importing the component into App.tsx and listing the items using the built-in [FlatList](https://reactnative.dev/docs/flatlist) component.

Replace the content of the App.tsx with the code below:

import React, {useState} from 'react';
import {SafeAreaView, StyleSheet, View, FlatList} from 'react-native';
import Header from './src/components/Header';
import AddItem, {IItem} from './src/components/AddItem';
import Item from './src/components/Item';
const App = () => {
  const [shoppingList, setShoppingList] = useState<IItem[]>([]);
  return (
    <SafeAreaView style={styles.container}>
      <Header title="Shopping List" />
      <View style={styles.contentWrapper}>
        <AddItem
          setShoppingList={setShoppingList}
          shoppingList={shoppingList}
        />
        <FlatList
          data={shoppingList}
          keyExtractor={(item, index) => `${item.item}-${index}`}
          renderItem={({item}) => (
            <Item item={item.item} quantity={item.quantity} />
          )}
        />
      </View>
    </SafeAreaView>
  );
};
const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#e8e7e3',
  },
  contentWrapper: {
    padding: 20,
  },
});
export default App;

Our app is now complete. On line 16, we use the FlatList component to render items from our list, and you can see how we use the Item component in the renderItem prop for FlatList.

Here’s a demo of our new app:

Gif of items being added in a shopping list app

Conclusion

This article was meant to be a light introduction to how you can benefit from the features of TypeScript in your React Native applications. The shopping list app was simple enough to show you how to use TypeScript without making things complex.

You can take things a step further by implementing a feature to remove items from the list.

Learn more about TypeScript with the official documentation, and find the source code for this project here.

: Full visibility into your web apps

LogRocket is a frontend application monitoring solution that lets you replay problems as if they happened in your own browser. Instead of guessing why errors happen, or asking users for screenshots and log dumps, LogRocket lets you replay the session to quickly understand what went wrong. It works perfectly with any app, regardless of framework, and has plugins to log additional context from Redux, Vuex, and @ngrx/store.

In addition to logging Redux actions and state, LogRocket records console logs, JavaScript errors, stacktraces, network requests/responses with headers + bodies, browser metadata, and custom logs. It also instruments the DOM to record the HTML and CSS on the page, recreating pixel-perfect videos of even the most complex single-page apps.

.
Edmund Ekott Frontend engineer who specializes in building complex UIs with JavaScript and CSS.

One Reply to “Using TypeScript with React Native”

  1. I would love to have an updated React-native and typescript and be able to render on the web. I know there is one here already but I followed it and the webpack config is not working and I couldn’t fix it.

Leave a Reply