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.
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.
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:
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.
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.
Open the folder in your code editor and run the commands below (depending on your machine) to start the project:
# 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.
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.
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:
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.
LogRocket is a React Native monitoring solution that helps you reproduce issues instantly, prioritize bugs, and understand performance in your React Native apps.
LogRocket also helps you increase conversion rates and product usage by showing you exactly how users are interacting with your app. LogRocket's product analytics features surface the reasons why users don't complete a particular flow or don't adopt a new feature.
Start proactively monitoring your React Native apps — try LogRocket for free.
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 and mobile apps.
Hey there, want to help make our blog better?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowUnderstanding and supporting pinch, text, and browser zoom significantly enhances the user experience. Let’s explore a few ways to do so.
Playwright is a popular framework for automating and testing web applications across multiple browsers in JavaScript, Python, Java, and C#. […]
Matcha, a famous green tea, is known for its stress-reducing benefits. I wouldn’t claim that this tea necessarily inspired the […]
Backdrop and background have similar meanings, as they both refer to the area behind something. The main difference is that […]
2 Replies to "Using TypeScript with React Native"
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.
Is the typescript template still existing? Use the command results to an error while fetching the template