How to access DOM elements in a React TypeScript app

TypeScript allows us to define types for our React components, props, state, and other objects, assisting us in catching type-related errors early in the development process. While we can solely use React, using TypeScript offers advantages in terms of type safety, code development, and better integration with the React framework, leading to more robust and maintainable code.

Application

Manipulating or interacting with DOM elements rendered by a React component is a common task. For example, any web app involves an extensive use of forms, and working with forms requires us to read DOM (Document Object Model) values. These values can be from an input field and be stored in the state, validated on the frontend, or sent to the server. For such scenarios, the concept of React refs is used to access DOM elements associated with a React component.

Note: To learn more about how React refs work, visit this Answer.

Expected output

Before diving into the main steps of this task, let’s see what the result will look like after the implementation is complete.

Step-by-step method

Using React refs in TypeScript requires a couple of additional changes to the standard React code we see when working with a basic React application. Le’s take a look at the steps to implement these changes successfully.

Step 1: Creating a React app with TypeScript

If we don’t have an existing React app with TypeScript on our systems, we can create one using the following commands in the terminal, one after the other. We can use any project name instead of the <project-name> tag.

npx create-react-app <project-name> --template typescript

Note: The --template typescript flag is responsible for setting up a React app with TypeScript support including its configuration filesDetails of this are present in the tsconfig.json file when the app is created. and build processes.Details of this are found in the package.json file when the app is created.

Step 2: Creating a Form component

Next, we will create a simple form component via a Form class, defining interfaces that specify the shapeThe shape of a component’s state refers to the structure or blueprint of the state object with its properties defined. of the component’s state. The Form class extends the React.Component, making it a component class. The code for this is shown below:

//The interfaces states and properties specify the additional properties that the form component accepts
// Define an interface for the component's state
interface States {
name: string;
}
// Define an empty interface for the component's properties
interface Properties {}
// Create a class component named "Form" that extends the "Component" class
export class Form extends Component<Properties, States> {
// Declare a private field to hold a reference to the name input element
private nameInputRef: RefObject<HTMLInputElement>;
// superclass constructor function that takes "props" as an argument which is the constructor of the parent class
constructor(props: Properties) {
// Call the constructor of the parent class "Component" with the "props"
super(props);
// Initialize the component's state with an empty "name" property
this.state = {
name: '',
};
// Create a ref for the name input element
this.nameInputRef = React.createRef();
}

Note: We must always remember to make the necessary imports when creating any type of React Typescript component.

Step 3: Modifying the contents of the Form component

To the code written in the step above, we will add a form, which will be inside a render() method containing an input field and a submit button. An onSubmit event handler will be added to the form that is triggered after the text is entered in the input text field and is submitted after pressing the “Submit” button on the webpage. The code for this is shown below.

// Render method to define the component's UI
render() {
return (
<form className="commentForm" onSubmit={(e) => this.handleSubmit(e)}>
<h1>Using React Refs in TypeScript</h1>
<input
type="text"
placeholder="Enter text"
ref={this.nameInputRef} // Bind the ref to the name input element
/>
<button type="submit">Submit</button>
{/* Extra features present: Display the entered text if there is a name in the state */}
{this.state.name && (
<div className="text">
<p>Entered text: </p>{this.state.name}
<div>
{/* Rendering a button to refresh the page */}
<button className="re" onClick={() => this.handleRefresh()}>Refresh</button>
</div>
</div>
)}
</form>
);
}

By using this render() method, all DOM manipulations can be done via the handleSubmit() method that takes the event object as a parameter of the React.FormEvent type. Here, React refs come in where DOM elements are accessed with the ref name, this.nameInputRef. It finds the actual DOM element associated with the ref and updates the form components’ state as a result. The code to implement this is shown below.

handleSubmit(e: FormEvent) {
// Prevent the default form submission behavior
e.preventDefault();
// Check if the name input element exists in the ref
if (this.nameInputRef.current) {
// Get the value of the input element
const inputValue = this.nameInputRef.current.value;
// Log the input value to the console
console.log(inputValue);
// Update the component's state with the input value
this.setState({
name: inputValue,
});
}
}
The handleSubmit() function for form submission

To make sure the added functionality in our render() function works, we will implement a simple handleRefresh() event handler function that is triggered when the “Refresh” button appears on the webpage when we enter some text.

// Extra event handler function for refreshing the page as a part of additional functionality of the form
handleRefresh(){
// Reload the current window
window.location.reload()
}
The handleRefresh() function for refreshing the webpage

After combining these functions, the Form.tsx file will look as follows:

import React, { Component, FormEvent, RefObject } from 'react';
//form.css file imported for extra webpage styling
import "./form.css"
//the interfaces states and properties specify the additional properties that the form component accepts
// Define an interface for the component's state
interface States {
name: string;
}
// Define an empty interface for the component's properties
interface Properties {}
// Create a class component named "Form" that extends the "Component" class
export class Form extends Component<Properties, States> {
// Declare a private field to hold a reference to the name input element
private nameInputRef: RefObject<HTMLInputElement>;
// superclass constructor function that takes "props" as an argument which is the constructor of the parent class
constructor(props: Properties) {
// Call the constructor of the parent class "Component" with the "props"
super(props);
// Initialize the component's state with an empty "name" property
this.state = {
name: '',
};
// Create a ref for the name input element
this.nameInputRef = React.createRef();
}
handleSubmit(e: FormEvent) {
// Prevent the default form submission behavior
e.preventDefault();
// Check if the name input element exists in the ref
if (this.nameInputRef.current) {
// Get the value of the input element
const inputValue = this.nameInputRef.current.value;
// Log the input value to the console
console.log(inputValue);
// Update the component's state with the input value
this.setState({
name: inputValue,
});
}
}
// Extra event handler function for refreshing the page as a part of additional functionality of the form
handleRefresh(){
// Reload the current window
window.location.reload()
}
// Render method to define the component's UI
render() {
return (
<form className="commentForm" onSubmit={(e) => this.handleSubmit(e)}>
<h1>Using React Refs in TypeScript</h1>
<input
type="text"
placeholder="Enter text"
ref={this.nameInputRef} // Bind the ref to the name input element
/>
<button type="submit">Submit</button>
{/* Extra features present: Display the entered text if there is a name in the state */}
{this.state.name && (
<div className="text">
<p>Entered text: </p>{this.state.name}
<div>
{/* Rendering a button to refresh the page */}
<button className="re" onClick={() => this.handleRefresh()}>Refresh</button>
</div>
</div>
)}
</form>
);
}
}

Step 4: Importing the form component

Finally, we will include the form component inside our App.tsx file present in the /src directory to prevent any import errors at runtime. The code for this is shown below.

import React from 'react';
import './App.css';
import { Form } from './Form';
//function that includes the form component inside <div> tags
function App() {
return (
<div className="App">
<Form/>
</div>
);
}
export default App;

We will then start the application with the npm start command in the terminal.

Code example

All of the aforementioned steps can be seen within the code application below. Run it to see the app in action!

import React, { Component, FormEvent, RefObject } from 'react';
import "./form.css"
//the interfaces states and properties specify the additional properties that the form component accepts

// Define an interface for the component's state
interface States {
  name: string;
}

// Define an empty interface for the component's properties
interface Properties {}

// Create a class component named "Form" that extends the "Component" class
export class Form extends Component<Properties, States> {

  // Declare a private field to hold a reference to the name input element
  private nameInputRef: RefObject<HTMLInputElement>;

  // superclass constructor function that takes "props" as an argument which is the constructor of the parent class
  constructor(props: Properties) {
    // Call the constructor of the parent class "Component" with the "props"
    super(props);
    // Initialize the component's state with an empty "name" property
    this.state = {
      name: '',
    };
    // Create a ref for the name input element
    this.nameInputRef = React.createRef();
  }

  handleSubmit(e: FormEvent) {
    // Prevent the default form submission behavior
    e.preventDefault();
    // Check if the name input element exists in the ref
    if (this.nameInputRef.current) {
      // Get the value of the input element
      const inputValue = this.nameInputRef.current.value;
      // Log the input value to the console
      console.log(inputValue);
      // Update the component's state with the input value
      this.setState({
        name: inputValue,
      });
    }
  }

  // Extra event handler function for refreshing the page as a part of additional functionality of the form 
  handleRefresh(){
    // Reload the current window
    window.location.reload()
  }

  // Render method to define the component's UI
  render() {
    return (
      <form className="commentForm" onSubmit={(e) => this.handleSubmit(e)}>
        <h1>Using React Refs in TypeScript</h1>
        <input
          type="text"
          placeholder="Enter text"
          ref={this.nameInputRef} // Bind the ref to the name input element
        />
        <button type="submit">Submit</button>
        {/* Extra features present: Display the entered text if there is a name in the state */}
        {this.state.name && (
          <div className="text">
            <p>Entered text: </p>{this.state.name}
            <div>
              {/* Rendering a button to refresh the page */}
              <button className="re" onClick={() => this.handleRefresh()}>Refresh</button>   
            </div>
          </div>
        )}
      </form>
    );
  }
}

Note: Another way of verifying that the entered text was returned at the output is by going into the console after right-clicking and selecting the inspect option.

Conclusion

Using React refs in React TypeScript applications allows us to interact with the DOM elements in React components in a safer and more controlled manner, providing better type checking than its predecessor, the React.findDOMNode() method. This method had been considered a performance bottleneck because it often bypassed React’s virtual DOM reconciliationIt is a process used in web development framework and libraries like React to efficiently update the user interface in response to changes in application data., resulting in unexpected behavior and performance issues.

Free Resources

Copyright ©2025 Educative, Inc. All rights reserved