Connecting a Frontend Framework (React) with TypeScript

Table of Contents

  • Introduction
  • Prerequisites
  • Step 1: Set Up a New React Project with TypeScript
  • Step 2: Install Required Dependencies
  • Step 3: Set Up Folder Structure
  • Step 4: Create Functional Components with TypeScript
  • Step 5: Props and State in TypeScript
  • Step 6: Handling Events with TypeScript in React
  • Step 7: Working with Context API and Redux (Optional)
  • Step 8: TypeScript Types for React Router
  • Step 9: Type Safety in useEffect and useState Hooks
  • Step 10: Conclusion

Introduction

React, a popular JavaScript library for building user interfaces, works seamlessly with TypeScript. TypeScript adds type safety to React components, making development more robust, easier to maintain, and less error-prone. By combining React and TypeScript, developers can enhance their workflow, improve code readability, and catch potential bugs during the development phase.

In this guide, we will show how to set up and work with React and TypeScript, step-by-step, covering various aspects such as component creation, state management, event handling, and more.


Prerequisites

Before getting started, ensure you have the following tools installed:

  1. Node.js: Install from Node.js website.
  2. npm (Node Package Manager): This comes with Node.js and is used to install dependencies.

You should also have some familiarity with the basics of React and TypeScript.


Step 1: Set Up a New React Project with TypeScript

The first step is to create a new React project using TypeScript. You can easily set up a React and TypeScript project using Create React App:

npx create-react-app my-react-ts-app --template typescript

This will set up a new React application pre-configured with TypeScript.

Once the installation is complete, navigate to your project directory:

cd my-react-ts-app

Step 2: Install Required Dependencies

You should already have most of the required dependencies installed by Create React App, but here are some additional packages you may need:

  • React Router for handling routes (if building a multi-page app).
  • Redux or Context API for state management.

For React Router:

npm install react-router-dom
npm install @types/react-router-dom --save-dev

For Redux (if needed for state management):

npm install react-redux @reduxjs/toolkit
npm install @types/react-redux --save-dev

Step 3: Set Up Folder Structure

Create the basic folder structure for better organization. In a large project, it’s a good practice to organize your project into components, assets, and other necessary directories.

Example folder structure:

src/
├── assets/
├── components/
├── pages/
├── services/
├── App.tsx
└── index.tsx

This will help in keeping the project modular and scalable as it grows.


Step 4: Create Functional Components with TypeScript

React components can be written as functional components with TypeScript. Here’s an example of creating a simple Button component:

src/components/Button.tsx:

import React from 'react';

// Define type for props
type ButtonProps = {
label: string;
onClick: () => void;
};

const Button: React.FC<ButtonProps> = ({ label, onClick }) => {
return (
<button onClick={onClick}>{label}</button>
);
};

export default Button;

In this example:

  • ButtonProps defines the expected properties (label and onClick).
  • React.FC (Function Component) automatically infers children and enforces the type safety for props.

Step 5: Props and State in TypeScript

To handle props and state in TypeScript, you can explicitly define types for the state and props for your components.

Example with State:

src/components/Counter.tsx:

import React, { useState } from 'react';

// Define type for state
type CounterState = {
count: number;
};

const Counter: React.FC = () => {
const [state, setState] = useState<CounterState>({ count: 0 });

const increment = () => setState({ count: state.count + 1 });
const decrement = () => setState({ count: state.count - 1 });

return (
<div>
<h1>{state.count}</h1>
<button onClick={increment}>Increment</button>
<button onClick={decrement}>Decrement</button>
</div>
);
};

export default Counter;

Here:

  • The state (count) is typed with CounterState.
  • TypeScript ensures that you only set the state object with a count property of type number.

Step 6: Handling Events with TypeScript in React

When dealing with events like onClick, onChange, or onSubmit, TypeScript can provide type safety. Below is an example of handling a form submission event.

Example:

import React, { useState } from 'react';

const Form: React.FC = () => {
const [name, setName] = useState<string>('');

const handleSubmit = (event: React.FormEvent) => {
event.preventDefault();
console.log('Submitted name:', name);
};

return (
<form onSubmit={handleSubmit}>
<input
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
/>
<button type="submit">Submit</button>
</form>
);
};

export default Form;

Here, React.FormEvent ensures that handleSubmit handles the event correctly with type safety.


Step 7: Working with Context API and Redux (Optional)

If your app needs to manage state globally, you can use React Context API or Redux. Below is a simple example of using the Context API for global state management.

Example (Context API):

  1. Create Context:
import React, { createContext, useState, useContext } from 'react';

interface ThemeContextType {
theme: string;
setTheme: (theme: string) => void;
}

const ThemeContext = createContext<ThemeContextType | undefined>(undefined);

export const ThemeProvider: React.FC = ({ children }) => {
const [theme, setTheme] = useState<string>('light');

return (
<ThemeContext.Provider value={{ theme, setTheme }}>
{children}
</ThemeContext.Provider>
);
};

export const useTheme = (): ThemeContextType => {
const context = useContext(ThemeContext);
if (!context) {
throw new Error('useTheme must be used within a ThemeProvider');
}
return context;
};
  1. Using Context in a Component:
import React from 'react';
import { useTheme } from './ThemeContext';

const ThemeSwitcher: React.FC = () => {
const { theme, setTheme } = useTheme();

return (
<div>
<p>Current theme: {theme}</p>
<button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>
Toggle Theme
</button>
</div>
);
};

export default ThemeSwitcher;

Step 8: TypeScript Types for React Router

When using React Router, you can add type definitions for route parameters and queries.

Example:

import React from 'react';
import { BrowserRouter as Router, Route, Switch, useParams } from 'react-router-dom';

interface UserParams {
userId: string;
}

const User: React.FC = () => {
const { userId } = useParams<UserParams>();
return <div>User ID: {userId}</div>;
};

const App: React.FC = () => {
return (
<Router>
<Switch>
<Route path="/user/:userId" component={User} />
</Switch>
</Router>
);
};

export default App;

In this example, useParams<UserParams> provides type safety for route parameters.


Step 9: Type Safety in useEffect and useState Hooks

TypeScript also adds type safety to useEffect and useState hooks.

Example (useEffect with Fetch):

import React, { useState, useEffect } from 'react';

interface Data {
id: number;
name: string;
}

const FetchData: React.FC = () => {
const [data, setData] = useState<Data | null>(null);

useEffect(() => {
const fetchData = async () => {
const response = await fetch('https://api.example.com/data');
const result: Data = await response.json();
setData(result);
};

fetchData();
}, []);

return (
<div>
{data ? <p>{data.name}</p> : <p>Loading...</p>}
</div>
);
};

export default FetchData;

Here, the Data interface ensures the fetched data matches the expected type.


Step 10: Conclusion

You’ve now learned how to connect a React frontend with TypeScript, including:

  • Setting up a React app with TypeScript.
  • Creating typed functional components with props and state.
  • Handling events and managing global state with Context API or Redux.
  • Ensuring type safety when working with React Router, useEffect, and useState.

By combining React and TypeScript, you can improve your code quality, catch errors early, and make your React applications more maintainable and scalable. With these techniques, you’re well-equipped to develop large-scale, type-safe React applications.