TypeScript has been gaining a lot of popularity recently due to how it helps improve the development experience by catching errors and suggesting fixes prior to running the code. For this reason, TypeScript goes a long way towards preventing any runtime errors and reducing bugs.
TypeScript does this by extending JavaScript to add types and perform type checking during development, as a result, all type errors must be fixed prior to running the code, similar to languages such as Elm.
TypeScript is fairly great and in some cases, you may want to plug it into a codebase that already utilizes JavaScript, this can be used hand in hand with your JavaScript code or to gradually migrate your codebase to JavaScript. In this article, we’ll be looking at how we can use TypeScript with React and built components that are usable within both TypeScript and JavaScript files. To do this we’ll be building a simple display component that takes in some data and shows it to the user.
The Replay is a weekly newsletter for dev and engineering leaders.
Delivered once a week, it's your curated guide to the most important conversations around frontend dev, emerging AI tools, and the state of modern software.
In order to save time on the configuration of our developer environment, we will be bootstrapping our application with create-react-app and fortunately for us, it also happens to have a TypeScript template that we can use to quickly get up and running.
If you don’t have create-react-app installed, install it by running:
npm install -g create-react-app
With create-react-app already installed, create your application by running:
yarn create react-app my-app --template typescript
Get into your app’s directory and run using the yarn start command in your terminal.
We’ll be having some minimal routing in our application using react-router, so we will need to install some dependencies:
yarn add react-router-dom @types/react-router-dom
This will install two packages, react-router-dom provides the DOM bindings for react-router whereas @types/react-router-dom contains the type definitions for react-router-dom .
Now that we have the boilerplate for our application setup, create two new directories in your src folder, pages which will hold the two simple pages we will be having on our application and DisplayData which will house our component.
Each of these pages will use the same display component that we will be creating, with one of the pages implemented with JavaScript and the other implemented in TypeScript.
Inside the pages folder, create two files, WithTS.tsx and WithoutTS.jsx both these files will contain functional components that just pass some data into our reusable display component. Our JavaScript page will contain a component called DisplayWithoutTS which we’ll place in the WithoutTS.jsx and it will look like this:
import React from 'react'
import { DisplayData } from '../DisplayData'
const DisplayWithoutTS = () => {
const info = {
name: 'Anakin Skywalker',
alias: [ 'Darth Vader', 'The Chosen One'],
powers: ['Force Push', 'Force Pull', 'Force Vision'],
rating: 10
}
return <DisplayData data={info}/>
}
export { DisplayWithoutTS }
Our TypeScript implementation, a DisplayWithTS component, will be in the WithTs.tsx file and contain this code:
import React from 'react'
import { DisplayData } from '../DisplayData'
const DisplayWithTS = () => {
const info = {
name: 'Sheev Palpatine',
alias: [ 'Chancellor Palpatine', 'Emperor Palpatine', 'Darth Sidious'],
powers: ['Force Push', 'Force Pull', 'Force Vision', 'Force Lightning', 'Sith Storm', 'Essense Transfer'],
rating: 10
}
return <DisplayData data={info}/>
}
export { DisplayWithTS }
Now that we have our base pages set up, our next step is to set up our routing. Since we only have two routes, we’ll keep it simple and edit our App.tsx file:
import React from 'react';
import './App.css';
import { BrowserRouter, Route, Switch } from 'react-router-dom'
import { DisplayWithoutTS } from './pages/WithoutTS'
import { DisplayWithTS } from './pages/WithTS'
function App() {
return (
<div className="App">
<div>
<a href="/">With JS</a>
<br/>
<a href="/withts">With TS</a>
</div>
<br/><br/>
<BrowserRouter>
<Switch>
<Route exact path='/' component={DisplayWithoutTS}/>
<Route exact path='/withts' component={DisplayWithTS}/>
<Route exact path='*' >
<div>
404 Page Not Found
</div>
</Route>
</Switch>
</BrowserRouter>
</div>
);
}
export default App;
We have two links before the BrowserRouter component, these will let us switch between the two pages in our app. We used Switch to handle routing and handled 404 errors by matching them to the * path to display the error message for all unaccounted paths.
Before we set up our TypeScript compatibility, we need to build our component first. In theDisplayData directory, create two files, DisplayData.jsx and index.ts .
Let’s create our display component inside DisplayData.jsx :
import React from 'react'
import {shape, string, number, arrayOf} from 'prop-types'
const DisplayData = ({data}) => {
return (
<div>
<div>Name: {data.name}</div>
<div>Alias: {data.alias.join(', ')}</div>
<div>Powers: {data.powers.join(', ')}</div>
<div>Rating: {data.rating}</div>
</div>
)
}
DisplayData.propTypes = {
data: shape({
name: string,
alias: arrayOf(string),
powers: arrayOf(string),
rating: number,
})
}
DisplayData.defaultProps = {
data: {
name: '',
alias: [],
powers: [],
rating: null,
}
}
export { DisplayData }
Our component takes in a data prop which it uses to fill some information about a fictional character and display it to the user. We carry out prop validation with prop-types and, in this case, our component receives a single prop which is an object. We use shape to describe an object type in our prop validation. We then proceed to fill in the types of the contents of our object such as string and number for text strings and numerical values respectively. For the data that comes in as an array, we use arrayOf to define the contents of the array, this is great as it provides more specificity than simply declaring the data using the array type.
We then export this component inside index.ts to make it available for use within other files.
Now that we have our component set up and working, we need to add some TypeScript configuration in order to make sure type checking is handled by TypeScript when it is used within a TypeScript file (one with a .ts or .tsx extension). In order to make use of TypeScript’s type checking ability, we need to be familiar with the concept of Types and Interfaces, you can check out this great article covering both to help you get up to speed. Types basically specify the format of the various pieces of data we are passing in. Interfaces allow us to shape the values as well as fill the role of naming these types, and are a powerful way of defining contracts within your code as well as contracts with code outside of your project. We also need to be familiar with modules that allow us to encapsulate our added TypeScript into the existing React component and allow it to carry out type checking when used in a TypeScript file.
Incorporating TypeScript into the component is as easy as adding the following code to our index.ts file in the component folder:
interface DisplayDataProps {
data : {
name: String,
alias: String[],
powers: String[],
rating: Number,
}
}
declare module '.'{
export const DisplayData: React.FC<DisplayDataProps>
}
We have our interface declared as DisplayDataProps , the type definitions within our interface are very similar to the prop validation we have with prop-types that we have within our component but much cleaner. for object types, we simply map the object as it will be structured. Other basic types will be defined as they were with prop-types with the only difference being that the type name is capitalized, i.e. string will be String and number will be Number, etc.
You may also notice our type definition foralias and powers which are both arrays of strings and a little different from what we have in our validation with prop-types, rather than arrayOf(string) we have String[] to declare an array of strings. This is not only cleaner than the prop-types implementation but it also automatically returns an empty list when the value is undefined, this can come in handy as it provides automatic default props to a component.
Once our interface has been defined we need to declare the component as a TypeScript module, in order to inform TypeScript to use the defined interface when the component is imported into a .ts or .tsx file. We defined our DisplayData component within the declared module using React.FC (React.FunctionalComponent in full) to make its return type more explicitly and provide type checking and autocomplete for static properties like displayName, propTypes, and defaultProps. You can find more information about this on the React TypeScript cheatsheet.
And that’s it, we’re good to go! Since we already did all the prior setup all we have to do is rerun our app. Switching between the routes should display the data as fed to the application by the two pages as shown in the demo below.
You can play around with the type validations to make sure everything is being picked up as expected, changing any of them should break the application.
You are now equipped with what you need to make your component usable in codebases that have either JavaScript or TypeScript files. This can come in handy in many cases, be it migrating your codebase or building a compatible component library. TypeScript’s type checking comes in handy in development and is definitely something that makes TypeScript worth picking up. If you wish to take a look at the code from this application you can check out this GitHub repo.
LogRocket lets you replay user sessions, eliminating guesswork by showing exactly what users experienced. It captures console logs, errors, network requests, and pixel-perfect DOM recordings — compatible with all frameworks, and with plugins to log additional context from Redux, Vuex, and @ngrx/store.
With Galileo AI, you can instantly identify and explain user struggles with automated monitoring of your entire product experience.
Modernize how you understand your web and mobile apps — start monitoring for free.
Install LogRocket via npm or script tag. LogRocket.init() must be called client-side, not
server-side
$ npm i --save logrocket
// Code:
import LogRocket from 'logrocket';
LogRocket.init('app/id');
// Add to your HTML:
<script src="https://cdn.lr-ingest.com/LogRocket.min.js"></script>
<script>window.LogRocket && window.LogRocket.init('app/id');</script>

Vibe coding isn’t just AI-assisted chaos. Here’s how to avoid insecure, unreadable code and turn your “vibes” into real developer productivity.

GitHub SpecKit brings structure to AI-assisted coding with a spec-driven workflow. Learn how to build a consistent, React-based project guided by clear specs and plans.

:has(), with examplesThe CSS :has() pseudo-class is a powerful new feature that lets you style parents, siblings, and more – writing cleaner, more dynamic CSS with less JavaScript.

Kombai AI converts Figma designs into clean, responsive frontend code. It helps developers build production-ready UIs faster while keeping design accuracy and code quality intact.
Would you be interested in joining LogRocket's developer community?
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 now